Làm cách nào để sửa 'android.os.NetworkOnMainThreadException'?


2394

Tôi đã gặp lỗi khi chạy dự án Android cho RssReader.

Mã số:

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();

Và nó hiển thị lỗi dưới đây:

android.os.NetworkOnMainThreadException

Làm thế nào tôi có thể khắc phục vấn đề này?


131
Đọc bài đăng trên blog này trên NetworkOnMainThreadException để biết thêm thông tin. Nó giải thích tại sao điều này xảy ra trên Android 3.0 trở lên.
sư Adrian

6
Để được theo dõi nghi thức trước tiên hãy đọc về Yêu cầu mạng trong Android sau đó tôi khuyên bạn nên nghiên cứu "Volley".
Anuj Sharma

3
Có nhiều thư viện thay thế giải quyết vấn đề này. Nhiều người được liệt kê ở dưới cùng của trang này . Nếu bạn có nhiều hơn, chúng tôi sẽ lấy chúng :)
Snicolas

Bạn cần chạy các hoạt động trên một luồng riêng biệt với luồng chính (UI)
Naveed Ahmad

"Do lỗi trong các phiên bản trước của Android, hệ thống đã không gắn cờ ghi vào ổ cắm TCP trên luồng chính là vi phạm chế độ nghiêm ngặt. Android 7.0 đã sửa lỗi này. Các ứng dụng thể hiện hành vi này hiện ném android.os. NetworkOnMainThreadException. " - Vì vậy, một số người trong chúng ta đã không đạt được điều này cho đến gần đây! developer.android.com/about/versions/nougat/ khăn
Jay

Câu trả lời:


2547

Ngoại lệ này được đưa ra khi một ứng dụng cố gắng thực hiện một hoạt động mạng trên luồng chính của nó. Chạy mã của bạn trong AsyncTask:

class RetrieveFeedTask extends AsyncTask<String, Void, RSSFeed> {

    private Exception exception;

    protected RSSFeed doInBackground(String... urls) {
        try {
            URL url = new URL(urls[0]);
            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();
        } catch (Exception e) {
            this.exception = e;

            return null;
        } finally {
            is.close();
        }
    }

    protected void onPostExecute(RSSFeed feed) {
        // TODO: check this.exception
        // TODO: do something with the feed
    }
}

Cách thực hiện tác vụ:

Trong MainActivity.javatệp bạn có thể thêm dòng này trong oncreate()phương thức của bạn

new RetrieveFeedTask().execute(urlToRssFeed);

Đừng quên thêm nó vào AndroidManifest.xmltập tin:

<uses-permission android:name="android.permission.INTERNET"/>

37
Tôi nghĩ điều đáng chú ý ở đây là đoạn mã ở trên được cho là một lớp con (lớp bên trong), tốt nhất là riêng tư. Theo cách đó khi AsyncTask kết thúc, bạn vẫn có thể thao tác các bộ phận trong lớp.
chứng khó đọc

4
Trên thực tế tôi đã làm điều tương tự như u nêu trên nhưng m phải đối mặt với lỗi này java.lang.RuntimeException: Không thể tạo xử lý bên trong chủ đề đó đã không được gọi Looper.prepare ()
Dhruv Tyagi

68
Đây chính xác là câu trả lời sai. Tôi bắt gặp điều này mọi lúc trong mã người, và thật khó chịu khi phải sửa nó mọi lúc. AsyncTask không nên được sử dụng cho hoạt động mạng, vì nó gắn liền với hoạt động, nhưng không phải là vòng đời hoạt động. Xoay thiết bị với tác vụ này đang chạy sẽ gây ra ngoại lệ và làm hỏng ứng dụng của bạn. Thay vào đó, hãy sử dụng IntentService loại bỏ dữ liệu trong cơ sở dữ liệu sqlite.
Brill Pappin

5
Lưu ý, AsyncTask thường được sử dụng cho mỗi hoạt động mạng hoạt động khi không nên. vòng đời của nó không đồng bộ với hoạt động. Để tìm nạp dữ liệu, bạn nên sử dụng IntentService và cơ sở dữ liệu phía sau chế độ xem.
Brill Pappin

1
@BrillPappin, FWIW, Hướng dẫn dành cho nhà phát triển Android này sử dụng AsyncTaskvà chăm sóc thay đổi cấu hình.
HeyJude

677

Bạn hầu như luôn luôn nên chạy các hoạt động mạng trên một luồng hoặc như một tác vụ không đồng bộ.

Nhưng nó thể loại bỏ hạn chế này và bạn ghi đè lên hành vi mặc định, nếu bạn sẵn sàng chấp nhận những hậu quả.

Thêm vào:

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();

StrictMode.setThreadPolicy(policy); 

Trong lớp của bạn,

THÊM quyền này trong tệp manifest.xml của Android:    

<uses-permission android:name="android.permission.INTERNET"/>

Kết quả:

Ứng dụng của bạn sẽ (trong các khu vực kết nối internet không chính xác) trở nên không phản hồi và bị khóa, người dùng nhận thấy sự chậm chạp và phải thực hiện hành vi giết người, và bạn có nguy cơ người quản lý hoạt động giết ứng dụng của bạn và nói với người dùng rằng ứng dụng đã dừng.

Android có một số mẹo hay về thực hành lập trình tốt để thiết kế để đáp ứng: http://developer.android.com/reference/android/os/NetworkOnMainThreadException.html


456
Đây là một ý kiến ​​tồi. giải pháp là tránh IO mạng trên luồng chính (như câu trả lời được chấp nhận hiển thị).
MByD

74
Với điều này, bạn chỉ che giấu vấn đề thực sự của bạn.
Alex

28
@TwistedUmhima AsyncTask không thêm một trang mã, nó thêm 6 dòng (khai báo lớp, ghi chú chú thích, doInBackgroundkhai báo, 2 dấu ngoặc đóng và gọi đến execute()). Mặt khác, ngay cả một lần tìm nạp từ một trang web mà bạn đề cập cũng mang lại độ trễ đáng kể trong khả năng phản hồi của UI. Đừng lười biếng.
Zoltán

19
Tôi nghĩ rằng đây là giải pháp hoàn hảo nếu bạn đang tìm cách chạy một đoạn mã mẫu để xem có thứ gì đó hoạt động trước khi triển khai AsyncTask thích hợp không. Đó là lý do tại sao tôi nêu lên câu trả lời này, mặc dù như tất cả những người khác đã nói, điều này không nên được thực hiện trong một ứng dụng sản xuất, chỉ như một cách giải quyết nhanh chóng cho một bài kiểm tra dev.
hook82

94
Nâng cao. Câu trả lời này là chính xác, và đối với nhiều lập trình viên không ngây thơ cũng không ngu ngốc, nhưng chỉ cần yêu cầu một cuộc gọi SYNCHRONOUS (nghĩa là: điều này phải chặn ứng dụng ), đây chính xác là những gì cần thiết. Tôi rất vui khi Android mặc định ném ngoại lệ (IMHO là một việc rất "hữu ích"!) - nhưng tôi cũng rất vui khi nói "cảm ơn, nhưng - đây thực sự là những gì tôi dự định" và ghi đè nó
Adam

428

Tôi đã giải quyết vấn đề này bằng cách sử dụng một cái mới Thread.

Thread thread = new Thread(new Runnable() {

    @Override
    public void run() {
        try  {
            //Your code goes here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});

thread.start(); 

7
Thay vì tạo một luồng mới mỗi lần bạn muốn thực hiện thao tác mạng, bạn cũng có thể sử dụng một dịch vụ thực thi luồng duy nhất.
Alex Lockwood

66
Đơn giản nhưng nguy hiểm. Runnable ẩn danh có một tham chiếu ngầm đến lớp kèm theo (ví dụ Hoạt động hoặc Đoạn của bạn), ngăn không cho rác được thu thập cho đến khi luồng hoàn thành. Ít nhất bạn nên đặt mức độ ưu tiên thành Process.BACKGROUND, nếu không thì luồng này sẽ chạy ở mức ưu tiên giống như luồng chính / ui, liên quan đến các phương thức vòng đời và tốc độ khung hình ui (xem các cảnh báo trong nhật ký từ biên đạo múa).
Stevie

1
@Stevie làm thế nào để đặt ưu tiên? cả runnble và execorService đều không có phương thức setter như vậy
JK

1
@JK Cung cấp ExecutorService của bạn với một ThreadFactory tùy chỉnh và gọi Thread.setP Warriority trên chuỗi trước khi trả lại nó.
Stevie

1
"Sử dụng Chủ đề trực tiếp trong Android không được khuyến khích. Nó gây ra nhiều vấn đề hơn giải quyết" Bạn có quan tâm đến vấn đề đó không? Trên thực tế AsyncTask đang bị phản đối chính xác cho ... techyourchance.com/asynctask-deprecated
Fran Marzoa

170

Câu trả lời được chấp nhận có một số mặt trái đáng kể. Không nên sử dụng AsyncTask để kết nối mạng trừ khi bạn thực sự biết bạn đang làm gì. Một số mặt trái bao gồm:

  • AsyncTask được tạo dưới dạng các lớp bên trong không tĩnh có một tham chiếu ngầm đến đối tượng Activity kèm theo, bối cảnh của nó và toàn bộ hệ thống phân cấp View được tạo bởi hoạt động đó. Tham chiếu này ngăn Hoạt động khỏi rác được thu thập cho đến khi công việc nền của AsyncTask hoàn tất. Nếu kết nối của người dùng chậm và / hoặc tải xuống lớn, những rò rỉ bộ nhớ ngắn hạn này có thể trở thành vấn đề - ví dụ: nếu hướng thay đổi nhiều lần (và bạn không hủy tác vụ thực thi) hoặc người dùng điều hướng ra khỏi Hoạt động.
  • AsyncTask có các đặc điểm thực thi khác nhau tùy thuộc vào nền tảng mà nó thực thi trên: trước API cấp 4 AsyncT task thực thi ser serent trên một luồng nền đơn; từ API cấp 4 đến API cấp 10, AsyncT task thực thi trên nhóm tối đa 128 luồng; từ API cấp 11 trở đi, AsyncTask thực thi ser serent trên một luồng nền đơn (trừ khi bạn sử dụng executeOnExecutorphương thức quá tải và cung cấp một trình thực thi thay thế). Mã hoạt động tốt khi chạy ser seri trên ICS có thể bị hỏng khi được thực thi đồng thời trên Gingerbread, giả sử nếu bạn có các phụ thuộc thứ tự thực hiện vô tình.

Nếu bạn muốn tránh rò rỉ bộ nhớ ngắn hạn, có các đặc điểm thực thi được xác định rõ trên tất cả các nền tảng và có cơ sở để xây dựng xử lý mạng thực sự mạnh mẽ, bạn có thể muốn xem xét:

  1. Sử dụng một thư viện thực hiện tốt công việc này cho bạn - có một so sánh hay về các lib mạng trong câu hỏi này , hoặc
  2. Sử dụng một Servicehoặc IntentServicethay vào đó, có lẽ với một PendingIntentđể trả về kết quả thông qua onActivityResultphương thức Activity .

Phương pháp tiếp cận IntentService

Mặt trái:

  • Nhiều mã và độ phức tạp hơn AsyncTask, mặc dù không nhiều như bạn nghĩ
  • Sẽ xếp hàng yêu cầu và chạy chúng trên một đơn sợi nền. Bạn có thể dễ dàng kiểm soát điều này bằng cách thay thế IntentServicebằng một Servicetriển khai tương đương , có thể như thế này .
  • Ừm, tôi không thể nghĩ về bất kỳ ai khác ngay bây giờ

Mặt trái:

  • Tránh vấn đề rò rỉ bộ nhớ ngắn hạn
  • Nếu hoạt động của bạn khởi động lại trong khi các hoạt động mạng đang hoạt động, nó vẫn có thể nhận được kết quả tải xuống thông qua onActivityResultphương thức của nó
  • Nền tảng tốt hơn AsyncTask để xây dựng và sử dụng lại mã mạng mạnh mẽ. Ví dụ: nếu bạn cần thực hiện tải lên quan trọng, bạn có thể thực hiện việc đó AsyncTasktrong một Activity, nhưng nếu bối cảnh người dùng chuyển ra khỏi ứng dụng để thực hiện cuộc gọi điện thoại, hệ thống có thể tắt ứng dụng trước khi quá trình tải lên hoàn tất. Nó ít có khả năng giết chết một ứng dụng có hoạt động Service.
  • Nếu bạn sử dụng phiên bản đồng thời của riêng mình IntentService(như phiên bản tôi đã liên kết ở trên), bạn có thể kiểm soát mức độ đồng thời thông qua Executor.

Tóm tắt thực hiện

Bạn có thể thực hiện một IntentServiceđể thực hiện tải xuống trên một chủ đề nền khá dễ dàng.

Bước 1: Tạo một IntentServiceđể thực hiện tải xuống. Bạn có thể cho nó biết những gì cần tải xuống thông qua Intentthêm và chuyển nó PendingIntentđể sử dụng để trả kết quả về Activity:

import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Intent;
import android.util.Log;

import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

public class DownloadIntentService extends IntentService {

    private static final String TAG = DownloadIntentService.class.getSimpleName();

    public static final String PENDING_RESULT_EXTRA = "pending_result";
    public static final String URL_EXTRA = "url";
    public static final String RSS_RESULT_EXTRA = "url";

    public static final int RESULT_CODE = 0;
    public static final int INVALID_URL_CODE = 1;
    public static final int ERROR_CODE = 2;

    private IllustrativeRSSParser parser;

    public DownloadIntentService() {
        super(TAG);

        // make one and re-use, in the case where more than one intent is queued
        parser = new IllustrativeRSSParser();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        PendingIntent reply = intent.getParcelableExtra(PENDING_RESULT_EXTRA);
        InputStream in = null;
        try {
            try {
                URL url = new URL(intent.getStringExtra(URL_EXTRA));
                IllustrativeRSS rss = parser.parse(in = url.openStream());

                Intent result = new Intent();
                result.putExtra(RSS_RESULT_EXTRA, rss);

                reply.send(this, RESULT_CODE, result);
            } catch (MalformedURLException exc) {
                reply.send(INVALID_URL_CODE);
            } catch (Exception exc) {
                // could do better by treating the different sax/xml exceptions individually
                reply.send(ERROR_CODE);
            }
        } catch (PendingIntent.CanceledException exc) {
            Log.i(TAG, "reply cancelled", exc);
        }
    }
}

Bước 2: Đăng ký dịch vụ trong bảng kê khai:

<service
        android:name=".DownloadIntentService"
        android:exported="false"/>

Bước 3: Gọi dịch vụ từ Hoạt động, chuyển một đối tượng PendingResult mà Dịch vụ sẽ sử dụng để trả về kết quả:

PendingIntent pendingResult = createPendingResult(
    RSS_DOWNLOAD_REQUEST_CODE, new Intent(), 0);
Intent intent = new Intent(getApplicationContext(), DownloadIntentService.class);
intent.putExtra(DownloadIntentService.URL_EXTRA, URL);
intent.putExtra(DownloadIntentService.PENDING_RESULT_EXTRA, pendingResult);
startService(intent);

Bước 4: Xử lý kết quả trong onActivityResult:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == RSS_DOWNLOAD_REQUEST_CODE) {
        switch (resultCode) {
            case DownloadIntentService.INVALID_URL_CODE:
                handleInvalidURL();
                break;
            case DownloadIntentService.ERROR_CODE:
                handleError(data);
                break;
            case DownloadIntentService.RESULT_CODE:
                handleRSS(data);
                break;
        }
        handleRSS(data);
    }
    super.onActivityResult(requestCode, resultCode, data);
}

Dự án Github chứa dự án Android-Studio / Gradle hoạt động hoàn chỉnh có sẵn tại đây .


IntentService là cách chính xác để làm điều này, không phải nhổ neo vì AsyncTask chính xác là cách không làm điều đó.
Brill Pappin

3
@BrillPappin Tôi gần như hoàn toàn đồng ý và đã diễn đạt lại để nhấn mạnh những nhược điểm của AsyncTask. (Tôi vẫn nghĩ rằng có một số lượng rất nhỏ các trường hợp - nếu bạn thực sự biết bạn đang làm gì - thể sử dụng AsyncTask, nhưng câu trả lời được chấp nhận không chỉ ra bất kỳ nhược điểm nào và quá phổ biến của Android).
Stevie

1
Bạn có thực sự cần IllustrativeRSS? Nếu bạn không làm việc với công cụ RSS thì sao?
Cullub

Theo tôi, Google nên thay đổi cách triển khai thu gom rác của họ thay vì đặt berdon về phía lập trình viên. Đây là trách nhiệm của HĐH. Nếu một lập trình viên sử dụng IntentService hoặc Service để thực hiện công việc vì vấn đề cơ bản trong triển khai Android, sau một số năm, Google sẽ nói IntentService cũng là một thực tiễn tồi và gợi ý điều gì khác. Và câu chuyện này tiếp tục ... Vì vậy, các nhà phát triển Google nên giải quyết việc quản lý bộ nhớ xấu của Android chứ không phải lập trình viên.
saeed khalafinejad

Tôi chỉ định nói rằng: Câu trả lời này có vẻ giống như một cách để chia sẻ dự án hơn là một cách thay thế tốt hơn AsyncTask. Lỗi này có nghĩa là ngăn các nhà phát triển tụt lại UI, không nhất thiết phải lôi kéo họ vào rủi ro bảo mật với một loạt các ý định / dịch vụ.
Giỏ hàng bị bỏ rơi

144

Bạn không thể thực hiện I / O mạng trên luồng UI trên Honeycomb . Về mặt kỹ thuật, nó tốt trên các phiên bản trước của Android, nhưng nó là một ý tưởng thực sự xấu vì nó sẽ gây ra ứng dụng của bạn để ngừng đáp ứng, và có thể dẫn đến việc hệ điều hành tiêu diệt ứng dụng của bạn để được xấu cư xử. Bạn sẽ cần chạy quy trình nền hoặc sử dụng AsyncTask để thực hiện giao dịch mạng của mình trên chuỗi nền.

Có một bài viết về Threading không đau trên trang web dành cho nhà phát triển Android, đây là một giới thiệu tốt về điều này và nó sẽ cung cấp cho bạn một câu trả lời sâu sắc hơn nhiều so với có thể được cung cấp thực tế ở đây.


76
  1. Không sử dụngrictMode (chỉ trong chế độ gỡ lỗi)
  2. Không thay đổi phiên bản SDK
  3. Không sử dụng một chủ đề riêng biệt

Sử dụng Dịch vụ hoặc AsyncTask

Xem thêm Câu hỏi về Stack Overflow:

android.os.NetworkOnMainThreadException gửi email từ Android


8
Có lẽ đáng nhấn mạnh rằng nếu bạn sử dụng Dịch vụ, bạn vẫn sẽ cần tạo một luồng riêng - Cuộc gọi lại dịch vụ chạy trên luồng chính. Mặt khác, một IntentService chạy phương thức onHandleIntent của nó trên một luồng nền.
Stevie

bạn không nên sử dụng AsyncTask cho các hoạt động dài! Nguyên tắc chỉ định tối đa 2 đến 3 giây.
Dage

76

Thực hiện các hành động mạng trên một chủ đề khác

Ví dụ:

new Thread(new Runnable(){
    @Override
    public void run() {
        // Do network action in this function
    }
}).start();

Và thêm phần này vào AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>

5
Nhưng làm thế nào chúng ta có thể tìm ra khi luồng kết thúc trong này để chúng ta có thể thực hiện các tác vụ tiếp theo trong luồng UI? AsyncTask cung cấp phương tiện để làm điều đó. Có cách nào để làm điều tương tự bằng cách sử dụng các chủ đề runnable?
Piyush Soni

3
Nó sẽ xử lý mã của bạn từng bước một, vì vậy, khi kết thúc mã, bạn cần sử dụng trình xử lý trở lại luồng UI
henry4343

1
Bạn có thể sử dụng tác vụ không đồng bộ hoặc dịch vụ mục đích, vì nó thực thi trên luồng công nhân.
Chetan Chaudhari

63

Bạn tắt chế độ nghiêm ngặt bằng mã sau:

if (android.os.Build.VERSION.SDK_INT > 9) {
    StrictMode.ThreadPolicy policy = 
        new StrictMode.ThreadPolicy.Builder().permitAll().build();
    StrictMode.setThreadPolicy(policy);
}

Điều này không được khuyến khích : sử dụngAsyncTask giao diện.

Mã đầy đủ cho cả hai phương thức


2
Có lỗi ANR sẽ đến. có nghĩa là ứng dụng không phản hồi trong 5 giây.
Muhammad Mubashir

12
Đây là một câu trả lời thực sự xấu. Bạn không nên thay đổi chính sách của luồng mà để viết mã tốt hơn: không thực hiện các hoạt động mạng trên luồng chính!
shkschneider

@Sandeep Bạn và những người xem khác cũng nên đọc nó. stackoverflow.com/a/18335031
43270479

53

Các hoạt động dựa trên mạng không thể chạy trên luồng chính. Bạn cần chạy tất cả các tác vụ dựa trên mạng trên một luồng con hoặc triển khai AsyncTask.

Đây là cách bạn chạy một tác vụ trong một luồng con:

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation goes here
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();

1
Ẩn danh Runnable KHÔNG phải là cách tốt nhất, vì nó có một tham chiếu ngầm đến lớp kèm theo và ngăn không cho nó bị ed ed cho đến khi luồng hoàn thành! Ngoài ra, luồng này sẽ chạy ở mức Ưu tiên giống như luồng chính / Hoa Kỳ, tương ứng với các phương thức vòng đời và tốc độ khung hình UI!
Yousha Aleayoub

49

Đặt mã của bạn vào bên trong:

new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            // Your implementation
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}).start();

Hoặc là:

class DemoTask extends AsyncTask<Void, Void, Void> {

    protected Void doInBackground(Void... arg0) {
        //Your implementation
    }

    protected void onPostExecute(Void result) {
        // TODO: do something with the feed
    }
}

Người thứ hai sẽ tốt nhất so với người đầu tiên cho api trên 11 tuổi
Rohit Goswami

46

Điều này xảy ra trong Android 3.0 trở lên. Từ Android 3.0 trở lên, họ đã hạn chế sử dụng các hoạt động mạng (các chức năng truy cập Internet) để chạy trong luồng chính / luồng UI (những gì sinh ra từ phương thức tạo và tiếp tục của bạn trong hoạt động).

Điều này là để khuyến khích sử dụng các luồng riêng biệt cho các hoạt động mạng. Xem AsyncTask để biết thêm chi tiết về cách thực hiện các hoạt động mạng đúng cách.


46

Sử dụng chú thích Android là một tùy chọn. Nó sẽ cho phép bạn chỉ cần chạy bất kỳ phương thức nào trong một luồng nền:

// normal method
private void normal() {
    doSomething(); // do something in background
}

@Background
protected void doSomething() 
    // run your networking code here
}

Lưu ý, mặc dù nó cung cấp lợi ích của sự đơn giản và dễ đọc, nhưng nó có nhược điểm.


6
@Gavriel nó tạo ra các bản sao của tất cả mọi thứ bạn chú thích, cho dù đó là phương thức, hoạt động, đoạn, singleton, v.v., do đó, có gấp đôi mã và mất nhiều thời gian hơn để biên dịch nó. Nó cũng có thể có một số vấn đề do lỗi trong thư viện. Gỡ lỗi và tìm lỗi sẽ trở nên khó khăn hơn.
Oleksiy

43

Lỗi là do thực thi các hoạt động chạy dài trong luồng chính, Bạn có thể dễ dàng khắc phục sự cố bằng cách sử dụng AsynTask hoặc Chủ đề . Bạn có thể kiểm tra thư viện này AsyncHTTPClient để xử lý tốt hơn.

AsyncHttpClient client = new AsyncHttpClient();
client.get("http://www.google.com", new AsyncHttpResponseHandler() {

    @Override
    public void onStart() {
        // Called before a request is started
    }

    @Override
    public void onSuccess(int statusCode, Header[] headers, byte[] response) {
        // Called when response HTTP status is "200 OK"
    }

    @Override
    public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {
        // Called when response HTTP status is "4XX" (for example, 401, 403, 404)
    }

    @Override
    public void onRetry(int retryNo) {
        // Called when request is retried
    }
});

42

Bạn không nên thực hiện bất kỳ tác vụ tốn thời gian nào trên luồng chính (luồng UI), như mọi hoạt động mạng, I / O tệp hoặc các hoạt động cơ sở dữ liệu SQLite. Vì vậy, đối với loại hoạt động này, bạn nên tạo một luồng công nhân, nhưng vấn đề là bạn không thể trực tiếp thực hiện bất kỳ hoạt động nào liên quan đến giao diện người dùng từ luồng công nhân của bạn. Đối với điều đó, bạn phải sử dụng Handlervà vượt qua Message.

Để đơn giản hóa tất cả những điều này, Android cung cấp nhiều cách khác nhau, như AsyncTask, AsyncTaskLoader, CursorLoaderhoặc IntentService. Vì vậy, bạn có thể sử dụng bất kỳ trong số này theo yêu cầu của bạn.


40

Câu trả lời hàng đầu của spektom hoạt động hoàn hảo.

Nếu bạn đang viết AsyncTasknội tuyến và không mở rộng như một lớp, và trên hết, nếu có nhu cầu nhận được phản hồi từ bên ngoài AsyncTask, người ta có thể sử dụng get()phương thức như dưới đây.

RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();

(Từ ví dụ của anh ấy.)


5
sử dụng get()là một ý tưởng tồi ... nó làm cho AsyncTask "đồng bộ hóa" lại
Selvin

Có một cách khác tốt hơn ra khỏi nó? @Selvin
sivag1

2
Tôi nghĩ rằng bạn có thể thông tin chủ đề chính về kết quả. Ví dụ: gửi truyền phát đến chủ đề chính bao gồm cả kết quả.
Chine Gary

32

Điều này chỉ được ném cho các ứng dụng nhắm mục tiêu SDK Honeycomb hoặc cao hơn. Các ứng dụng nhắm mục tiêu các phiên bản SDK trước đó được phép kết nối mạng trên các chuỗi vòng lặp sự kiện chính của chúng.

Lỗi là cảnh báo SDK!


28

Đối với tôi nó là thế này:

<uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="10" />

Thiết bị tôi đã thử nghiệm ứng dụng của mình là 4.1.2, đó là SDK Phiên bản 16!

Đảm bảo phiên bản mục tiêu giống với Thư viện mục tiêu Android của bạn. Nếu bạn không chắc thư viện mục tiêu của mình là gì, hãy nhấp chuột phải vào Dự án của bạn -> Đường dẫn xây dựng -> Android và nó sẽ được đánh dấu.

Ngoài ra, như những người khác đã đề cập, bao gồm các quyền chính xác để truy cập Internet:

<uses-permission android:name="android.permission.INTERNET"/>

11
Hãy để tôi giải thích cho bạn những gì bạn đang làm ở đây: NetworkOnMainThreadExceptionNgười bảo vệ đang nói với bạn: đừng bắn vào chân bạn ... giải pháp của bạn là: hãy quay về quá khứ khi không có Người bảo vệ - bây giờ tôi có thể bắn vào tôi tự do đi bộ
Selvin

1
Tôi cũng áp dụng phương pháp này và không gặp vấn đề gì. Người bảo vệ đôi khi quá cầu kỳ.
FractalBob

25

Sử dụng cái này trong Hoạt động của bạn

    btnsub.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub

                    //Initialize soap request + add parameters
                    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME1);

                    //Use this to add parameters
                    request.addProperty("pincode", txtpincode.getText().toString());
                    request.addProperty("bg", bloodgroup.getSelectedItem().toString());

                    //Declare the version of the SOAP request
                    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);

                    envelope.setOutputSoapObject(request);
                    envelope.dotNet = true;

                    try {
                        HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

                        //this is the actual part that will call the webservice
                        androidHttpTransport.call(SOAP_ACTION1, envelope);

                        // Get the SoapResult from the envelope body.
                        SoapObject result = (SoapObject) envelope.getResponse();
                        Log.e("result data", "data" + result);
                        SoapObject root = (SoapObject) result.getProperty(0);
                        // SoapObject s_deals = (SoapObject) root.getProperty(0);
                        // SoapObject s_deals_1 = (SoapObject) s_deals.getProperty(0);
                        //

                        System.out.println("********Count : " + root.getPropertyCount());

                        value = new ArrayList<Detailinfo>();

                        for (int i = 0; i < root.getPropertyCount(); i++) {
                            SoapObject s_deals = (SoapObject) root.getProperty(i);
                            Detailinfo info = new Detailinfo();

                            info.setFirstName(s_deals.getProperty("Firstname").toString());
                            info.setLastName(s_deals.getProperty("Lastname").toString());
                            info.setDOB(s_deals.getProperty("DOB").toString());
                            info.setGender(s_deals.getProperty("Gender").toString());
                            info.setAddress(s_deals.getProperty("Address").toString());
                            info.setCity(s_deals.getProperty("City").toString());
                            info.setState(s_deals.getProperty("State").toString());
                            info.setPinecode(s_deals.getProperty("Pinecode").toString());
                            info.setMobile(s_deals.getProperty("Mobile").toString());
                            info.setEmail(s_deals.getProperty("Email").toString());
                            info.setBloodgroup(s_deals.getProperty("Bloodgroup").toString());
                            info.setAdddate(s_deals.getProperty("Adddate").toString());
                            info.setWaight(s_deals.getProperty("waight").toString());
                            value.add(info);
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    Intent intent = new Intent(getApplicationContext(), ComposeMail.class);
                    //intent.putParcelableArrayListExtra("valuesList", value);

                    startActivity(intent);
                }
            }).start();
        }
    });

23

Chỉ cần đánh vần một cái gì đó rõ ràng:

Chủ đề chính về cơ bản là chủ đề UI.

Vì vậy, nói rằng bạn không thể thực hiện các hoạt động mạng trong luồng chính có nghĩa là bạn không thể thực hiện các hoạt động mạng trong luồng UI, điều đó có nghĩa là bạn cũng không thể thực hiện các hoạt động mạng trong một *runOnUiThread(new Runnable() { ... }*khối bên trong một số luồng khác.

(Tôi vừa có một khoảnh khắc đau đầu cố gắng tìm ra lý do tại sao tôi lại gặp phải lỗi đó ở đâu đó ngoài chủ đề chính của tôi. Đây là lý do tại sao; chủ đề này đã giúp ích và hy vọng nhận xét này sẽ giúp người khác.)


22

Ngoại lệ này xảy ra do bất kỳ tác vụ nặng nào được thực hiện trên luồng chính nếu tác vụ thực hiện đó mất quá nhiều thời gian .

Để tránh điều này, chúng ta có thể xử lý nó bằng đề hoặc executers

Executors.newSingleThreadExecutor().submit(new Runnable() {
    @Override
    public void run() {
        // You can perform your task here.
    }
});

18

Có rất nhiều câu trả lời tuyệt vời cho câu hỏi này, nhưng rất nhiều thư viện tuyệt vời đã xuất hiện kể từ khi những câu trả lời đó được đăng. Điều này được dự định là một loại hướng dẫn người mới.

Tôi sẽ bao gồm một số trường hợp sử dụng để thực hiện các hoạt động mạng và một hoặc hai giải pháp cho mỗi trường hợp.

ReST qua HTTP

Điển hình là Json, có thể là XML hoặc một cái gì đó khác

Truy cập API đầy đủ

Giả sử bạn đang viết một ứng dụng cho phép người dùng theo dõi giá cổ phiếu, lãi suất và tỷ giá hối đoái. Bạn tìm thấy API Json trông giống như thế này:

http://api.example.com/stocks                       //ResponseWrapper<String> object containing a list of Srings with ticker symbols
http://api.example.com/stocks/$symbol               //Stock object
http://api.example.com/stocks/$symbol/prices        //PriceHistory<Stock> object
http://api.example.com/currencies                   //ResponseWrapper<String> object containing a list of currency abbreviation
http://api.example.com/currencies/$currency         //Currency object
http://api.example.com/currencies/$id1/values/$id2  //PriceHistory<Currency> object comparing the prices of the first currency (id1) to the second (id2)

Trang bị thêm từ Square

Đây là một lựa chọn tuyệt vời cho một API có nhiều điểm cuối và cho phép bạn khai báo các điểm cuối ReST thay vì phải mã hóa chúng riêng lẻ như với các thư viện khác như ion hoặc Volley. (trang web: http://sapes.github.io/retrofit/ )

Làm thế nào để bạn sử dụng nó với API tài chính?

xây dựng. nâng cấp

Thêm các dòng này vào cấp độ Mô-đun buid.gradle:

implementation 'com.squareup.retrofit2:retrofit:2.3.0' //retrofit library, current as of September 21, 2017
implementation 'com.squareup.retrofit2:converter-gson:2.3.0' //gson serialization and deserialization support for retrofit, version must match retrofit version

Tài chínhApi.java

public interface FinancesApi {
    @GET("stocks")
    Call<ResponseWrapper<String>> listStocks();
    @GET("stocks/{symbol}")
    Call<Stock> getStock(@Path("symbol")String tickerSymbol);
    @GET("stocks/{symbol}/prices")
    Call<PriceHistory<Stock>> getPriceHistory(@Path("symbol")String tickerSymbol);

    @GET("currencies")
    Call<ResponseWrapper<String>> listCurrencies();
    @GET("currencies/{symbol}")
    Call<Currency> getCurrency(@Path("symbol")String currencySymbol);
    @GET("currencies/{symbol}/values/{compare_symbol}")
    Call<PriceHistory<Currency>> getComparativeHistory(@Path("symbol")String currency, @Path("compare_symbol")String currencyToPriceAgainst);
}

Tài chínhApiBuilder

public class FinancesApiBuilder {
    public static FinancesApi build(String baseUrl){
        return new Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
                    .create(FinancesApi.class);
    }
}

Đoạn trích tài chính

FinancesApi api = FinancesApiBuilder.build("http://api.example.com/"); //trailing '/' required for predictable behavior
api.getStock("INTC").enqueue(new Callback<Stock>(){
    @Override
    public void onResponse(Call<Stock> stockCall, Response<Stock> stockResponse){
        Stock stock = stockCall.body();
        //do something with the stock
    }
    @Override
    public void onResponse(Call<Stock> stockCall, Throwable t){
        //something bad happened
    }
}

Nếu API của bạn yêu cầu Khóa API hoặc tiêu đề khác như mã thông báo người dùng, v.v. được gửi, Retrofit làm cho điều này trở nên dễ dàng (xem câu trả lời tuyệt vời này để biết chi tiết: https://stackoverflow.com/a/42899766/1024412 ).

Một lần truy cập ReST API

Giả sử bạn đang xây dựng một ứng dụng "thời tiết tâm trạng" tìm kiếm vị trí GPS của người dùng và kiểm tra nhiệt độ hiện tại trong khu vực đó và cho họ biết tâm trạng. Loại ứng dụng này không cần khai báo điểm cuối API; nó chỉ cần có thể truy cập một điểm cuối API.

Ion

Đây là một thư viện tuyệt vời cho loại truy cập này.

Vui lòng đọc câu trả lời tuyệt vời của msysmilu ( https://stackoverflow.com/a/28559884/1024412 )

Tải hình ảnh qua HTTP

Chuyền

Volley cũng có thể được sử dụng cho API ReST, nhưng do yêu cầu thiết lập phức tạp hơn nên tôi thích sử dụng Retrofit từ Square như trên ( http://sapes.github.io/retrofit/ )

Giả sử bạn đang xây dựng một ứng dụng mạng xã hội và muốn tải ảnh đại diện của bạn bè.

xây dựng. nâng cấp

Thêm dòng này vào cấp độ Mô-đun buid.gradle:

implementation 'com.android.volley:volley:1.0.0'

ImageFetch.java

Volley yêu cầu thiết lập nhiều hơn Retrofit. Bạn sẽ cần tạo một lớp như thế này để thiết lập RequestQueue, ImageLoader và ImageCache, nhưng nó không quá tệ:

public class ImageFetch {
    private static ImageLoader imageLoader = null;
    private static RequestQueue imageQueue = null;

    public static ImageLoader getImageLoader(Context ctx){
        if(imageLoader == null){
            if(imageQueue == null){
                imageQueue = Volley.newRequestQueue(ctx.getApplicationContext());
            }
            imageLoader = new ImageLoader(imageQueue, new ImageLoader.ImageCache() {
                Map<String, Bitmap> cache = new HashMap<String, Bitmap>();
                @Override
                public Bitmap getBitmap(String url) {
                    return cache.get(url);
                }
                @Override
                public void putBitmap(String url, Bitmap bitmap) {
                    cache.put(url, bitmap);
                }
            });
        }
        return imageLoader;
    }
}

user_view_dialog.xml

Thêm phần sau vào tệp xml bố cục của bạn để thêm hình ảnh:

<com.android.volley.toolbox.NetworkImageView
    android:id="@+id/profile_picture"
    android:layout_width="32dp"
    android:layout_height="32dp"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    app:srcCompat="@android:drawable/spinner_background"/>

Người dùng XemDialog.java

Thêm mã sau vào phương thức onCreate (Fragment, Activity) hoặc hàm tạo (Dialog):

NetworkImageView profilePicture = view.findViewById(R.id.profile_picture);
profilePicture.setImageUrl("http://example.com/users/images/profile.jpg", ImageFetch.getImageLoader(getContext());

Picasso

Một thư viện tuyệt vời khác từ Square. Vui lòng xem trang web để biết một số ví dụ tuyệt vời: http://sapes.github.io/picasso/


16

Nói một cách đơn giản,

KHÔNG LÀM VIỆC MẠNG TRONG UI THREAD

Ví dụ: nếu bạn thực hiện một yêu cầu HTTP, đó là một hành động mạng.

Giải pháp:

  1. Bạn phải tạo một chủ đề mới
  2. Hoặc sử dụng lớp AsyncTask

Đường:

Đặt tất cả các công việc của bạn bên trong

  1. run() phương pháp của chủ đề mới
  2. Hoặc doInBackground() phương thức của lớp AsyncTask.

Nhưng:

Khi bạn nhận được một cái gì đó từ phản hồi Mạng và muốn hiển thị nó trên chế độ xem của bạn (như thông báo phản hồi hiển thị trong TextView), bạn cần quay lại luồng UI .

Nếu bạn không làm điều đó, bạn sẽ nhận được ViewRootImpl$CalledFromWrongThreadException.

Làm thế nào để?

  1. Trong khi sử dụng AsyncTask, cập nhật chế độ xem từ onPostExecute()phương thức
  2. Hoặc gọi runOnUiThread()phương thức và xem cập nhật bên trong run()phương thức.

12

Bạn có thể di chuyển một phần mã của mình sang một luồng khác để giảm tải main threadvà tránh ANR , NetworkOnMainThreadException , IllegalStateException (ví dụ: Không thể truy cập cơ sở dữ liệu trên luồng chính vì nó có thể khóa UI trong một thời gian dài).

Có một số cách tiếp cận mà bạn nên chọn tùy thuộc vào tình huống

Java Chủ đề hoặc Android HandlerThread

Các luồng Java chỉ sử dụng một lần và chết sau khi thực hiện phương thức chạy của nó.

HandlerThread là một lớp tiện dụng để bắt đầu một chủ đề mới có một looper.

AsyncTask

AsyncTask được thiết kế để trở thành một lớp trợ giúp xung quanh ThreadHandler và không tạo thành một khung luồng chung. AsyncT task nên được sử dụng lý tưởng cho các hoạt động ngắn (tối đa vài giây.) Nếu bạn cần giữ các luồng hoạt động trong thời gian dài, bạn nên sử dụng các API khác nhau được cung cấp bởi gói java.util.conc hiện như Giám đốc điều hành , ThreadPoolExecutorFutureTask .

Thực hiện nhóm chủ đề ThreadPoolExecutor , lên lịchThreadPoolExecutor ...

Lớp ThreadPoolExecutor thực hiện ExecutorService cung cấp quyền kiểm soát tốt trên nhóm luồng (Ví dụ: kích thước nhóm lõi, kích thước nhóm tối đa, giữ thời gian tồn tại, v.v.)

Lên lịchThreadPoolExecutor - một lớp mở rộng ThreadPoolExecutor. Nó có thể lên lịch các nhiệm vụ sau một sự chậm trễ nhất định hoặc định kỳ.

Tương lai

Tuy nhiên, FutureTask thực hiện xử lý không đồng bộ, tuy nhiên, nếu kết quả chưa sẵn sàng hoặc quá trình xử lý chưa hoàn tất, việc gọi get () sẽ bị chặn luồng

AsyncTaskLoaders

AsyncTaskLoaders khi họ giải quyết rất nhiều vấn đề cố hữu với AsyncTask

Dịch vụ ý định

Đây là lựa chọn không chính xác để xử lý chạy lâu trên Android, một ví dụ điển hình là tải lên hoặc tải xuống các tệp lớn. Việc tải lên và tải xuống có thể tiếp tục ngay cả khi người dùng thoát khỏi ứng dụng và bạn chắc chắn không muốn chặn người dùng không thể sử dụng ứng dụng trong khi các tác vụ này đang diễn ra.

Công việc

Thực tế, bạn phải tạo một Dịch vụ và tạo một công việc bằng cách sử dụng JobInfo.Builder chỉ định tiêu chí của bạn khi nào nên chạy dịch vụ.

RxJava

Thư viện để soạn các chương trình không đồng bộ và dựa trên sự kiện bằng cách sử dụng các chuỗi có thể quan sát được.

Quân đoàn (Kotlin)

Ý chính của nó là, nó làm cho mã không đồng bộ trông rất giống với đồng bộ

Đọc thêm ở đây , ở đây , ở đây , ở đây


Làm việc cho tôi ... Tôi đã sử dụng AsyncTask rộng rãi, nhưng khi một tác vụ đang chạy, một tác vụ khác sẽ chờ. Tôi muốn giải quyết điều này. Bây giờ làm việc với execonexecutor. Chúng ta hãy xem nó sẽ hoạt động như thế nào trên các thiết bị bộ nhớ thấp.
Suraj Shingade

Vui lòng xem phương thức: asyncTask.executeOnExecutor (AsyncTask.THREAD_POOL_EXECUTOR, params); để chạy nhiệm vụ của bạn đồng thời
yoAlex5

10

Mặc dù ở trên có một nhóm giải pháp khổng lồ, không ai đề cập com.koushikdutta.ion: https://github.com/koush/ion

Nó cũng không đồng bộrất đơn giản để sử dụng:

Ion.with(context)
.load("http://example.com/thing.json")
.asJsonObject()
.setCallback(new FutureCallback<JsonObject>() {
   @Override
    public void onCompleted(Exception e, JsonObject result) {
        // do stuff with the result or error
    }
});

10

Các giải pháp mới ThreadAsyncTask đã được giải thích.

AsyncTasklý tưởng nên được sử dụng cho các hoạt động ngắn. Bình thường Threadlà không thích hợp cho Android.

Hãy xem giải pháp thay thế bằng HandlerThreadHandler

Xử lý

Lớp tiện dụng để bắt đầu một chủ đề mới có một looper. Các looper sau đó có thể được sử dụng để tạo các lớp xử lý. Lưu ý rằng start()vẫn phải được gọi.

Xử lý:

Trình xử lý cho phép bạn gửi và xử lý các đối tượng Message và Runnable được liên kết với MessageQueue của một luồng. Mỗi phiên bản Handler được liên kết với một luồng và hàng đợi thông báo của luồng đó. Khi bạn tạo một Trình xử lý mới, nó bị ràng buộc với hàng đợi luồng / thông báo của luồng đang tạo ra nó - từ thời điểm đó, nó sẽ gửi tin nhắn và runnable đến hàng đợi tin nhắn đó và thực hiện chúng khi chúng ra khỏi tin nhắn xếp hàng.

Giải pháp:

  1. Tạo nên HandlerThread

  2. Gọi start()vềHandlerThread

  3. Tạo Handlerbằng cách lấy LoopertừHanlerThread

  4. Nhúng mã liên quan đến hoạt động Mạng của bạn vào Runnableđối tượng

  5. Gửi Runnablenhiệm vụ đếnHandler

Đoạn mã mẫu, địa chỉ nào NetworkOnMainThreadException

HandlerThread handlerThread = new HandlerThread("URLConnection");
handlerThread.start();
handler mainHandler = new Handler(handlerThread.getLooper());

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Log.d("Ravi", "Before IO call");
            URL page = new URL("http://www.google.com");
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ( (line =  buff.readLine()) != null) {
                text.append(line + "\n");
            }
            Log.d("Ravi", "After IO call");
            Log.d("Ravi",text.toString());

        }catch( Exception err){
            err.printStackTrace();
        }
    }
};
mainHandler.post(myRunnable);

Ưu điểm của việc sử dụng phương pháp này:

  1. Tạo mới Thread/AsyncTaskcho mỗi hoạt động mạng là tốn kém. Nó Thread/AsyncTasksẽ bị hủy và được tạo lại cho các hoạt động Mạng tiếp theo. Nhưng với HandlerHandlerThreadcách tiếp cận, bạn có thể gửi nhiều hoạt động mạng (dưới dạng tác vụ Runnable) cho HandlerThreadtừng người bằng cách sử dụng Handler.

8

RxAndroidlà một giải pháp thay thế tốt hơn cho vấn đề này và nó giúp chúng tôi tránh khỏi những rắc rối khi tạo chủ đề và sau đó đăng kết quả lên chủ đề giao diện người dùng Android. Chúng ta chỉ cần xác định các luồng trên đó các tác vụ cần được thực hiện và mọi thứ được xử lý bên trong.

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);
    }
});
  1. Bằng cách chỉ định (Schedulers.io()), RxAndroid sẽ chạy getFavoriteMusicShows() trên một luồng khác.

  2. Bằng cách sử dụng, AndroidSchedulers.mainThread()chúng tôi muốn quan sát điều này có thể quan sát được trên luồng UI, tức là chúng tôi muốn onNext()cuộc gọi lại của chúng tôi được gọi trên luồng UI


8

Chuỗi chính là luồng UI và bạn không thể thực hiện thao tác trong luồng chính có thể chặn tương tác người dùng. Bạn có thể giải quyết điều này theo hai cách:

Buộc thực hiện nhiệm vụ trong luồng chính như thế này

StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(threadPolicy);

Hoặc tạo một trình xử lý đơn giản và cập nhật chủ đề chính nếu bạn muốn.

Runnable runnable;
Handler newHandler;

newHandler = new Handler();
runnable = new Runnable() {
    @Override
    public void run() {
         try {
            //update UI
        } catch (Exception e) {
            e.printStackTrace();
        } 
    }
};
newHandler.post(runnable);

Và để dừng sử dụng luồng:

newHandler.removeCallbacks(runnable);

Để biết thêm thông tin kiểm tra điều này: Luồng không đau


Cám ơn. Phiên bản 1 giúp khi thêm hành động đầu tiên trong onCreate.
Ingo

7

Những công việc này. Chỉ cần làm cho câu trả lời của Dr.Luiji đơn giản hơn một chút.

new Thread() {
    @Override
    public void run() {
        try {
            //Your code goes here
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}.start();

7

Trên Android, các hoạt động mạng không thể chạy trên luồng chính. Bạn có thể sử dụng Thread, AsyncTask (tác vụ chạy ngắn), Dịch vụ (tác vụ chạy dài) để thực hiện các hoạt động mạng.


7

Truy cập tài nguyên mạng từ luồng chính (UI) gây ra ngoại lệ này. Sử dụng một luồng riêng hoặc AsyncTask để truy cập tài nguyên mạng để tránh sự cố này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.