J'ai eu une erreur lors de l'exécution de mon projet Android pour RssReader.
Code:
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();
Et cela montre l'erreur ci-dessous:
Android.os.NetworkOnMainThreadException
Comment puis-je résoudre ce problème?
Cette exception est levée lorsqu'une application tente d'effectuer une opération de mise en réseau sur son thread principal. Exécutez votre code dans 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
}
}
Comment exécuter la tâche:
Dans le fichier MainActivity.Java
, vous pouvez ajouter cette ligne dans votre méthode oncreate()
new RetrieveFeedTask().execute(urlToRssFeed);
N'oubliez pas d'ajouter ceci dans le fichier AndroidManifest.xml
:
<uses-permission Android:name="Android.permission.INTERNET"/>
Vous devez presque toujours exécuter des opérations réseau sur un thread ou en tant que tâche asynchrone.
Mais est possible de supprimer cette restriction et de remplacer le comportement par défaut si vous êtes prêt à accepter les conséquences.
Ajouter:
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
Dans votre classe,
et
AJOUTEZ cette permission dans le fichier Android manifest.xml:
<uses-permission Android:name="Android.permission.INTERNET"/>
Conséquences:
Votre application (dans les zones de connexion Internet irrégulière) ne répond plus et se verrouille, l'utilisateur perçoit la lenteur et doit tuer de force, et vous risquez que le responsable de l'activité tue votre application et dis à l'utilisateur que celle-ci s'est arrêtée.
Android a quelques bons conseils sur les bonnes pratiques de programmation pour concevoir la réactivité: http://developer.Android.com/reference/Android/os/NetworkOnMainThreadException.html
J'ai résolu ce problème en utilisant une nouvelle Thread
.
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
La réponse acceptée comporte des inconvénients importants. Il est déconseillé d'utiliser AsyncTask pour la mise en réseau, sauf si vous vraiment savez ce que vous faites. Certains des inconvénients incluent:
executeOnExecutor
et fournissez un autre exécuteur). Le code qui fonctionne correctement lorsqu’il est exécuté en série sur ICS peut se rompre lorsqu’il est exécuté simultanément sur Gingerbread, par exemple si vous avez des dépendances d’ordre d’exécution par inadvertance.Si vous souhaitez éviter les fuites de mémoire à court terme, si vous avez des caractéristiques d’exécution bien définies sur toutes les plates-formes et une base permettant de créer une gestion réseau vraiment robuste, vous voudrez peut-être prendre en compte:
Service
ou IntentService
, peut-être avec un PendingIntent
pour renvoyer le résultat via la méthode onActivityResult
de l'Activité.Les inconvénients:
AsyncTask
, mais pas autant que vous ne le pensezIntentService
par une implémentation Service
équivalente, peut-être comme celle-ci .Sur les côtés:
onActivityResult
.AsyncTask
dans un Activity
, mais si le contexte de l'utilisateur quitte l'application pour prendre un appel téléphonique, le système peut tuer le application avant la fin du téléchargement. Il est moins probable} de tuer une application avec un Service
actif.IntentService
(comme celle que j'ai liée ci-dessus), vous pouvez contrôler le niveau de simultanéité via Executor
.Vous pouvez implémenter un IntentService
pour effectuer des téléchargements sur un seul thread en arrière-plan assez facilement.
Étape 1: Créez un IntentService
pour effectuer le téléchargement. Vous pouvez lui indiquer le contenu à télécharger via Intent
extra, et lui transmettre un PendingIntent
à utiliser pour renvoyer le résultat au 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);
}
}
}
Étape 2: inscrivez le service dans le manifeste:
<service
Android:name=".DownloadIntentService"
Android:exported="false"/>
Étape 3: Appelez le service depuis l'activité en passant un objet PendingResult que le service utilisera pour renvoyer le résultat:
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);
Étape 4: Gérez le résultat dans 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);
}
Un projet github contenant un projet Android/Studio complet et fonctionnel est disponible ici .
Vous ne pouvez pas effectuer de réseau I/O sur le thread d'interface utilisateur sur Honeycomb . Techniquement, est possible sur les versions précédentes d’Android, mais c’est une très mauvaise idée, car votre application cessera de répondre, et le système d’exploitation risque de tuer votre application parce qu’elle se comporte mal. Vous devrez exécuter un processus en arrière-plan ou utiliser AsyncTask pour effectuer votre transaction réseau sur un thread en arrière-plan.
Il existe un article sur Indolore Threading sur le site du développeur Android, qui est une bonne introduction à cela, et il vous fournira une profondeur de réponse bien meilleure que celle qui peut être fournie de manière réaliste ici.
Utiliser un service ou une tâche asynchrone
Voir aussi la question du débordement de pile:
Android.os.NetworkOnMainThreadException envoyer un email d'Android
Faire les actions du réseau sur un autre thread
Par exemple:
new Thread(new Runnable(){
@Override
public void run() {
// Do network action in this function
}
}).start();
Et ajoutez ceci à AndroidManifest.xml
<uses-permission Android:name="Android.permission.INTERNET"/>
Vous désactivez le mode strict en utilisant le code suivant:
if (Android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
Ceci n'est pas recommandé: utilisez l'interface AsyncTask
.
Les opérations basées sur le réseau ne peuvent pas être exécutées sur le thread principal. Vous devez exécuter toutes les tâches réseau sur un thread enfant ou implémenter AsyncTask.
Voici comment vous exécutez une tâche dans un thread enfant:
new Thread(new Runnable(){
@Override
public void run() {
try {
// Your implementation goes here
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
Mettez votre code à l'intérieur:
new Thread(new Runnable(){
@Override
public void run() {
try {
// Your implementation
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
Ou:
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
}
}
Utiliser Annotations Android est une option. Cela vous permettra d’exécuter simplement n’importe quelle méthode dans un fil d’arrière-plan:
// normal method
private void normal() {
doSomething(); // do something in background
}
@Background
protected void doSomething()
// run your networking code here
}
Notez que, bien qu’il offre des avantages de simplicité et de lisibilité, il présente des inconvénients.
Cela se produit dans Android 3.0 et supérieur. À partir d'Android 3.0 ou ultérieur, ils ont restreint l'utilisation des opérations réseau (fonctions qui accèdent à Internet) dans le thread principal/thread d'interface utilisateur (ce qui est généré par vos méthodes de création et de reprise dans l'activité).
Ceci encourage l'utilisation de threads distincts pour les opérations réseau. Voir AsyncTask pour plus de détails sur la manière d’exécuter correctement les activités du réseau.
Vous ne devez effectuer aucune tâche fastidieuse sur le thread principal (thread d'interface utilisateur), comme toute opération de réseau, d'entrée-sortie de fichier ou d'opération de base de données SQLite. Donc, pour ce type d'opération, vous devez créer un thread de travail, mais le problème est que vous ne pouvez pas effectuer directement d'opération liée à l'interface utilisateur à partir de votre thread de travail. Pour cela, vous devez utiliser Handler
et passer le Message
.
Pour simplifier toutes ces choses, Android propose différentes méthodes, telles que AsyncTask
, AsyncTaskLoader
, CursorLoader
ou IntentService
. Vous pouvez donc utiliser ces options en fonction de vos besoins.
L'erreur est due à l'exécution de longues opérations dans le thread principal. Vous pouvez facilement résoudre le problème en utilisant AsynTask ou Thread . Vous pouvez extraire cette bibliothèque AsyncHTTPClient pour une meilleure gestion.
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
}
});
Le top réponse de spektom fonctionne parfaitement.
Si vous écrivez le AsyncTask
inline et ne prolongez pas en tant que classe, et s'il est nécessaire d'obtenir une réponse de la AsyncTask
, vous pouvez utiliser la méthode get()
comme ci-dessous.
RSSFeed feed = new RetreiveFeedTask().execute(urlToRssFeed).get();
(De son exemple.)
Ceci n'est émis que pour les applications ciblant le Honeycomb SDK ou une version ultérieure. Les applications ciblant des versions antérieures du SDK sont autorisées à mettre en réseau leurs threads de boucle d'événements principaux.
Pour moi c'était ça:
<uses-sdk
Android:minSdkVersion="8"
Android:targetSdkVersion="10" />
L'appareil sur lequel je testais mon application était la version 4.1.2, qui correspond à la version 16 du SDK!
Assurez-vous que la version cible est la même que votre bibliothèque cible Android. Si vous ne savez pas quelle est votre bibliothèque cible, cliquez avec le bouton droit de la souris sur votre projet -> Chemin de construction -> Android , et vous devriez sélectionner celui qui est coché.
En outre, comme d'autres l'ont mentionné, incluez les autorisations appropriées pour accéder à Internet:
<uses-permission Android:name="Android.permission.INTERNET"/>
Juste pour épeler quelque chose de manière explicite:
Le fil principal est essentiellement le fil de l'interface utilisateur.
Dire que vous ne pouvez pas effectuer d'opérations de réseau dans le thread principal signifie que vous ne pouvez pas effectuer d'opérations de réseau dans le thread d'interface utilisateur, ce qui signifie que vous ne pouvez pas effectuer d'opérations de réseau dans un bloc *runOnUiThread(new Runnable() { ... }*
dans un autre thread.
(J'ai juste eu un long moment de réflexion en essayant de comprendre pourquoi j'avais cette erreur ailleurs que dans mon fil principal. C'est pourquoi; ce fil a aidé; et j'espère que ce commentaire aidera quelqu'un d'autre.)
Cette exception est due à une tâche lourde effectuée sur le thread principal si cette tâche d'exécution prend trop de temps.
Pour éviter cela, nous pouvons le gérer en utilisant threads ou executers
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
// You can perform your task here.
}
});
Utilisez ceci dans votre activité
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();
}
});
En des mots simples,
NE TRAVAILLEZ PAS EN RESEAU DANS LE FIL D'UI
Par exemple, si vous faites une requête HTTP, il s’agit d’une action réseau.
Solution:
Façon:
Mettez toutes vos oeuvres à l'intérieur
run()
méthode du nouveau threaddoInBackground()
méthode de la classe AsyncTask.Mais:
Lorsque vous recevez quelque chose de la réponse du réseau et que vous souhaitez l'afficher dans votre vue (comme un message de réponse affiché dans TextView), vous devez revenir au thread UI.
Si vous ne le faites pas, vous obtiendrez ViewRootImpl$CalledFromWrongThreadException
.
Comment?
onPostExecute()
runOnUiThread()
méthode et mettez à jour la vue dans la méthode run()
.Il y a déjà beaucoup de bonnes réponses à cette question, mais beaucoup de bonnes bibliothèques ont été publiées depuis leur publication. Ceci est conçu comme une sorte de guide débutant.
Je couvrirai plusieurs cas d’utilisation pour effectuer des opérations sur le réseau et a solution ou deux pour chacun.
_ {Typiquement Json, peut être XML ou autre chose)
Supposons que vous écriviez une application qui permet aux utilisateurs de suivre les cours des actions, les taux d’intérêt et les taux de change actuels. Vous trouvez une API Json qui ressemble à ceci:
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)
C'est un excellent choix pour une API avec plusieurs noeuds finaux et vous permet de déclarer les noeuds finaux ReST au lieu d'avoir à les coder individuellement comme avec d'autres bibliothèques comme ion ou Volley. (site web: http://square.github.io/retrofit/ )
Comment l'utilisez-vous avec l'API Finances?
build.gradle
Ajoutez ces lignes à votre buid.gradle de niveau Module:
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
FinancesApi.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);
}
FinancesApiBuilder
public class FinancesApiBuilder {
public static FinancesApi build(String baseUrl){
return new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(FinancesApi.class);
}
}
Extrait de fragment de Finances
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
}
}
Si votre API nécessite l'envoi d'une clé API ou d'un autre en-tête, tel qu'un jeton d'utilisateur, etc., Retrofit facilite les choses (voir cette réponse géniale pour plus de détails: https://stackoverflow.com/a/42899766/ 1024412 ).
Supposons que vous construisez une application «météo d'humeur» qui recherche la position GPS des utilisateurs, vérifie la température actuelle dans cette zone et leur indique l'ambiance. Ce type d'application n'a pas besoin de déclarer les points de terminaison de l'API; il doit simplement pouvoir accéder à un point de terminaison d'API.
C'est une excellente bibliothèque pour ce type d'accès.
Veuillez lire l'excellente réponse de msysmilu ( https://stackoverflow.com/a/28559884/1024412 )
Volley peut également être utilisé pour les API ReST, mais en raison de la configuration plus complexe requise, je préfère utiliser Retrofit from Square comme ci-dessus ( http://square.github.io/retrofit/ )
Supposons que vous construisez une application de réseau social et que vous souhaitez charger les photos de profil d'amis.
build.gradle
Ajoutez cette ligne à votre buid.gradle de niveau module:
implementation 'com.Android.volley:volley:1.0.0'
ImageFetch.Java
Volley nécessite plus de configuration que Retrofit. Vous devrez créer une classe comme celle-ci pour configurer un RequestQueue, un ImageLoader et un ImageCache, mais ce n'est pas si grave:
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
Ajoutez les éléments suivants à votre fichier XML de mise en page pour ajouter une image:
<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"/>
UserViewDialog.Java
Ajoutez le code suivant à la méthode onCreate (Fragment, Activity) ou au constructeur (Dialog):
NetworkImageView profilePicture = view.findViewById(R.id.profile_picture);
profilePicture.setImageUrl("http://example.com/users/images/profile.jpg", ImageFetch.getImageLoader(getContext());
Une autre excellente bibliothèque de Square. Veuillez consulter le site pour quelques exemples intéressants: http://square.github.io/picasso/
Bien que ci-dessus, il existe un énorme pool de solutions, personne n’a mentionné com.koushikdutta.ion
: https://github.com/koush/ion
C'est aussi asynchrone et très simple à utiliser:
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
}
});
Les nouvelles solutions Thread
et AsyncTask ont déjà été expliquées.
AsyncTask
devrait idéalement être utilisé pour de courtes opérations. Thread
normal n'est pas préférable pour Android.
Découvrez une autre solution utilisant HandlerThread et Handler
HandlerThread
Classe pratique pour démarrer un nouveau thread qui a une boucle. Le boucleur peut ensuite être utilisé pour créer des classes de gestionnaires. Notez que
start()
doit toujours être appelé.
Gestionnaire:
Un gestionnaire vous permet d’envoyer et de traiter les objets Message et Runnable associés au MessageQueue d’un thread. Chaque instance de gestionnaire est associée à un seul thread et à la file de messages de ce thread. Lorsque vous créez un nouveau gestionnaire, il est lié au thread/à la file d'attente de messages du thread qui le crée - à partir de ce moment, il distribuera les messages et les exécutables à cette file d'attente et les exécutera au fur et à mesure qu'ils sortent du message. queue.
Solution:
Créer HandlerThread
Appelez start()
au HandlerThread
Créez Handler
en obtenant Looper
à partir de HanlerThread
Incorporez le code associé à votre opération réseau dans l'objet Runnable
Soumettez la tâche Runnable
à Handler
Exemple d'extrait de code portant l'adresse 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);
Avantages d'utiliser cette approche:
Thread/AsyncTask
pour chaque opération de réseau coûte cher. Le Thread/AsyncTask
sera détruit et recréé pour les prochaines opérations sur le réseau. Toutefois, avec les approches Handler
et HandlerThread
, vous pouvez soumettre de nombreuses opérations réseau (en tant que tâches exécutables) à une seule variable HandlerThread
en utilisant Handler
. L'accès aux ressources réseau à partir du thread principal (UI) est à l'origine de cette exception. Utilisez un thread séparé ou AsyncTask pour accéder à une ressource réseau afin d'éviter ce problème.
Sur Android, les opérations réseau ne peuvent pas être exécutées sur le thread principal. Vous pouvez utiliser Thread, AsyncTask (tâches à exécution courte), Service (tâches à exécution longue) pour effectuer des opérations réseau.
Cela marche. Je viens de simplifier un peu la réponse de Dr.Luiji.
new Thread() {
@Override
public void run() {
try {
//Your code goes here
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
RxAndroid
est une autre meilleure solution à ce problème et nous évite les tracas liés à la création de threads, puis à la publication des résultats sur un thread 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);
}
});
En spécifiant (Schedulers.io())
, RxAndroid exécutera getFavoriteMusicShows()
sur un autre thread.
En utilisant AndroidSchedulers.mainThread()
, nous souhaitons observer cet observable sur le thread d'interface utilisateur, c'est-à-dire que nous souhaitons que notre rappel onNext()
soit appelé sur le thread d'interface utilisateur.
Vous pouvez déplacer une partie de votre code dans un autre thread pour décharger le main thread
et éviter d’obtenir ANR , NetworkOnMainThreadException , IllegalStateException (par exemple, ne peut pas accéder à la base de données sur le thread principal car potentiellement verrouiller l’UI pendant une longue période).
Certaines approches à choisir dépendent de la situation.
Java Thread ou Android HandlerThread
Les threads Java sont à usage unique et meurent après l'exécution de la méthode d'exécution.
HandlerThread est une classe pratique pour démarrer un nouveau thread avec une boucle.
AsyncTask est conçu pour être une classe d'assistance autour de Thread et Handler et ne constitue pas un framework de thread générique. AsyncTasks devrait idéalement être utilisé pour des opérations courtes (quelques secondes au maximum). Si vous devez maintenir les threads en cours d'exécution pendant de longues périodes, il est vivement recommandé d'utiliser les différentes API fournies par le package Java.util.concurrent, telles que Executor, ThreadPoolExecutor et FutureTask.
Implémentation de pool de threads ThreadPoolExecutor , ScheduledThreadPoolExecutor ...
Classe ThreadPoolExecutor qui implémente ExecutorService qui donne un contrôle précis sur le pool de threads (par exemple, taille du pool principal, taille maximale du pool, durée de conservation, etc.)
ScheduledThreadPoolExecutor - une classe qui étend ThreadPoolExecutor. Il peut planifier des tâches après un délai donné ou périodiquement.
FutureTask effectue un traitement asynchrone. Toutefois, si le résultat n'est pas encore prêt ou si le traitement n'est pas terminé, l'appel de get () bloquera le thread.
AsyncTaskLoaders car ils résolvent beaucoup de problèmes inhérents à AsyncTask
C'est le choix de fait pour le traitement de longue durée sur Android, un bon exemple serait de télécharger ou de télécharger des fichiers volumineux. Le téléchargement et le téléchargement peuvent continuer même si l'utilisateur quitte l'application et vous ne souhaitez certainement pas l'empêcher de pouvoir utiliser l'application pendant l'exécution de ces tâches.
Effectivement, vous devez créer un service et créer un travail à l'aide de JobInfo.Builder qui spécifie vos critères pour savoir quand exécuter le service.
Bibliothèque permettant de composer des programmes asynchrones et basés sur des événements en utilisant des séquences observables.
Coroutines (Kotlin)
L’essentiel est que le code asynchrone ressemble tellement à un code synchrone.
Lire la suite ici:
8 façons de faire un traitement asynchrone dans Android et compter
L’évolution de l’accès au réseau Android
Utilisation d'un pool de threads sous Android
Vous n'êtes pas autorisé à implémenter des opérations réseau sur le thread d'interface utilisateur sous Android. Vous devrez utiliser la classe AsyncTask pour effectuer des opérations liées au réseau, telles que l'envoi d'une demande d'API, le téléchargement d'une image à partir d'une URL, etc. et en utilisant les méthodes de rappel d'AsyncTask, vous pouvez obtenir le résultat de la méthode onPostExecute et vous serez dans le fil de l'interface utilisateur. peut peupler l'interface utilisateur avec des données de service Web ou quelque chose comme ça.
Exemple: supposons que vous vouliez télécharger une image depuis une URL: https://www.samplewebsite.com/sampleimage.jpg
Solution utilisant AsyncTask: sont respectivement.
public class MyDownloader extends AsyncTask<String,Void,Bitmap>
{
@Override
protected void onPreExecute() {
// Show progress dialog
super.onPreExecute();
}
@Override
protected void onPostExecute(Bitmap bitmap) {
//Populate Ui
super.onPostExecute(bitmap);
}
@Override
protected Bitmap doInBackground(String... params) {
// Open URL connection read bitmaps and return form here
return result;
}
@Override
protected void onProgressUpdate(Void... values) {
// Show progress update
super.onProgressUpdate(values);
}
}
}
Remarque: N'oubliez pas d'ajouter l'autorisation Internet dans le fichier manifeste Android. Cela fonctionnera comme un charme. :)
Il existe un autre moyen très pratique de résoudre ce problème - utilisez les fonctionnalités de simultanéité de rxJava. Vous pouvez exécuter n'importe quelle tâche en arrière-plan et publier les résultats sur le thread principal de manière très pratique. Ces résultats seront donc transmis à la chaîne de traitement.
Le premier conseil de réponse vérifié consiste à utiliser AsynTask. Oui, c'est une solution, mais elle est obsolète de nos jours, car il existe de nouveaux outils.
String getUrl() {
return "SomeUrl";
}
private Object makeCallParseResponse(String url) {
return null;
//
}
private void processResponse(Object o) {
}
La méthode getUrl fournit l'adresse URL et sera exécutée sur le thread principal.
makeCallParseResponse (..) - effectue un travail réel
processResponse (..) - gérera le résultat sur le thread principal.
Le code pour l'exécution asynchrone ressemblera à ceci:
rx.Observable.defer(new Func0<rx.Observable<String>>() {
@Override
public rx.Observable<String> call() {
return rx.Observable.just(getUrl());
}
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.map(new Func1<String, Object>() {
@Override
public Object call(final String s) {
return makeCallParseResponse(s);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
@Override
public void call(Object o) {
processResponse(o);
}
},
new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
// Process error here, it will be posted on
// the main thread
}
});
Comparée à AsyncTask, cette méthode permet de changer de planificateur un nombre arbitraire de fois (par exemple, récupérer des données sur un planificateur et traiter ces données sur un autre (par exemple, Scheduler.computation ()). Vous pouvez également définir vos propres planificateurs.
Pour utiliser cette bibliothèque, incluez les lignes suivantes dans votre fichier build.gradle:
compile 'io.reactivex:rxjava:1.1.5'
compile 'io.reactivex:rxandroid:1.2.0'
La dernière dépendance inclut la prise en charge du planificateur .mainThread ().
Il y a un excellent ebook pour rx-Java .
Vous pouvez soit utiliser KOTLIN
et ANKO
.
Kotlin est la nouvelle langue officielle de Android vous en trouverez plus ici https://kotlinlang.org/docs/tutorials/kotlin-Android.html
Anko bibliothèque prise en charge pour Kotlin dans Android, certains documents ici https://github.com/Kotlin/anko
La solution qui est vraiment utile et ne contient que quelques lignes de code écrites par @AntonioLeiva https://antonioleiva.com/anko-background-kotlin-Android/
doAsync {
var result = runLongTask()
uiThread {
toast(result)
}
}
NetworkOnMainThread
survient lorsque vous exécutez un travail en arrière-plan sur UI Thread
; vous devez donc exécuter votre longTask job
en arrière-plan. Vous pouvez le faire en utilisant cette méthode et Kotlin avec Anko dans votre application Android.
J'ai résolu ce problème d'une manière simple ...
J'ai ajouté après oncreate
StrictMode.enableDefaults();
et résolu ceci.
Ou
utilisez Service
ou AsyncTask
pour résoudre ce problème
Remarque:
Do not change SDK version
Do not use a separate thread
Pour plus, vérifiez ceci .
Cette exception est levée lorsqu'une application tente d'effectuer une opération de mise en réseau sur son thread principal . Si votre tâche a duré plus de cinq secondes, la fermeture est forcée.
Exécutez votre code dans AsyncTask
:
class RetrieveFeedTask extends AsyncTask<String, Void, Boolean> {
protected RSSFeed doInBackground(String... urls) {
// TODO: Connect
}
protected void onPostExecute(RSSFeed feed) {
// TODO: Check this.exception
// TODO: Do something with the feed
}
}
Android.os.NetworkOnMainThreadException est levé lorsque des opérations réseau sont effectuées sur le thread principal. Vous feriez mieux de faire cela dans AsyncTask pour supprimer cette exception. Écris-le comme ceci:
new AsyncTask<Void,String,String>(){
@Override
protected Void doInBackground(Void... params) {
// Perform your network operation.
// Get JSON or XML string from the server.
// Store in a local variable (say response) and return.
return response;
}
protected void onPostExecute(String results){
// Response returned by doInBackGround() will be received
// by onPostExecute(String results).
// Now manipulate your jason/xml String(results).
}
}.execute();
}
Nous pouvons également utiliser RxJava pour déplacer les opérations du réseau vers un fil d’arrière-plan. Et c'est assez simple aussi.
webService.doSomething(someData)
.subscribeOn(Schedulers.newThread())-- This for background thread
.observeOn(AndroidSchedulers.mainThread()) -- for callback on UI
.subscribe(result -> resultText.setText("It worked!"),
e -> handleError(e));
Vous pouvez faire beaucoup plus de choses avec RxJava.Voici quelques liens pour RxJava. N'hésitez pas à creuser.
Le thread principal est le thread d'interface utilisateur et vous ne pouvez pas effectuer d'opération dans le thread principal susceptible de bloquer l'interaction de l'utilisateur. Vous pouvez résoudre ce problème de deux manières:
Forcer à faire la tâche dans le fil principal comme ceci
StrictMode.ThreadPolicy threadPolicy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(threadPolicy);
Ou créez un gestionnaire simple et mettez à jour le thread principal si vous le souhaitez.
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);
Et pour arrêter le fil, utilisez:
newHandler.removeCallbacks(runnable);
Pour plus d’informations, consultez ceci: Filetage sans effort
Vous pouvez également résoudre ce problème en utilisant le mode strict en utilisant le code ci-dessous. C'est aussi une alternative à la résolution de ce problème.
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
Mais la meilleure pratique serait d'utiliser AsyncTask.
Comment réparer Android.os.NetworkOnMainThreadException
Qu'est-ce que NetworkOnMainThreadException:
Dans Android, toutes les opérations de l'interface utilisateur doivent être effectuées sur le fil de l'interface utilisateur (fil principal). Si nous effectuons des opérations en arrière-plan ou une opération du réseau sur le thread principal, nous risquons que cette exception se produise et que l'application ne réponde pas.
Comment le réparer:
Pour éviter ce problème, vous devez utiliser un autre thread pour les opérations en arrière-plan ou les opérations réseau, comme utiliser asyncTask et utiliser une bibliothèque pour les opérations réseau telles que Volley, AsyncHttp, etc.
L'exception NetworkOnMainThread se produit car vous avez appelé une opération de réseau sur le thread par défaut, c'est-à-dire le thread d'interface utilisateur. Selon la version Android Android 3 (Honeycomb) qui n’est pas autorisé, vous devez appeler une opération réseau en dehors du thread principal.
Vous pouvez utiliser AsyncTask, IntentService ou créer votre propre thread et appel à l'intérieur de la méthode d'exécution . Pour plus d'informations, visitez la page Connexion au réseau.
Faites ceci en arrière-plan en utilisant AsycTask
Java
class NetworkThread extends AsyncTask<String, Void, String> {
protected Void doInBackground(String... arg0) {
//Your implementation
}
protected void onPostExecute(String result) {
// TODO: do something with the feed
}
}
Appelle où tu as besoin
new NetworkThread().execute("Your URL here");
Kotlin
internal class MyNetworkTask : AsyncTask<String, Void, RSSFeed>() {
override fun doInBackground(vararg urls: String): RSSFeed? {
try {
// download
// prepare RSSFeeds
return RSSFeeds
} catch (e: Exception) {
//handle exception
return null
}
}
override fun onPostExecute(feed: RSSFeed) {
// TODO: check this.exception
// TODO: do something with the feed
}
}
Appeler à Kotlin
MyNetworkTask().execute(url)
Vous ne pouvez pas appeler le réseau sur le thread principal ou le thread UI. Sur Android, si vous souhaitez appeler le réseau, il existe deux options:
Personnellement je préfère asynctask. Pour plus d'informations, vous pouvez consulter ce lien .
Comme Android travaille sur un seul thread, vous ne devez effectuer aucune opération de réseau sur le thread principal. Il y a plusieurs façons d'éviter cela.
Utilisez la méthode suivante pour effectuer une opération réseau
N'utilisez jamais StrictMode.setThreadPolicy (policy) , car cela gèlera votre interface utilisateur et ce n'est pas du tout une bonne idée.
Vous pouvez réellement commencer un nouveau sujet, j'avais déjà ce problème et je l'ai résolu de cette façon.
internal class RetrieveFeedTask : AsyncTask<String, Void, RSSFeed>() {
override fun doInBackground(vararg urls: String): RSSFeed? {
try {
// download
// prepare RSSFeeds
return RSSFeeds
} catch (e: Exception) {
//handle exception
return null
}
}
override fun onPostExecute(feed: RSSFeed) {
// TODO: check this.exception
// TODO: do something with the feed
}
}
Exemple d'appel,
RetrieveFeedTask().execute(url)
Vous pouvez utiliser Kotlin-coroutines
class YoutActivity : AppCompatActivity, CoroutineScope {
override fun onCreate(...) {
launch { yourHeavyMethod() }
}
suspend fun yourHeavyMethod() {
async{ yourNetworkCall() }.await()
...
...
}
}
Vous pouvez suivre ce guide.
J'ai eu un problème similaire, je viens d'utiliser ce qui suit dans la méthode de création de votre activité.
//allow strict mode
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
et cela a bien fonctionné.
À compter de 2018, je recommanderais d'utiliser RxJava dans Kotlin pour la récupération réseau. Un exemple simple est ci-dessous.
Single.fromCallable {
// Your Network Fetching Code
Network.fetchHttp(url)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
// What you need to do with your result on the view
result -> view.updateScreen(result)
}
Différentes options:
utiliser un thread exécutable Java normal pour traiter une tâche réseau et peut utiliser runOnUIThread () pour mettre à jour l'interface utilisateur
la tâche intentservice/async peut être utilisée si vous souhaitez mettre à jour l'interface utilisateur après avoir obtenu une réponse du réseau.
Ne travaillez jamais longtemps sur le fil de l'interface utilisateur, ce travail peut être communication avec serveur, lecture/écriture sur fichier, etc. Ces tâches doivent être sur un fil d'arrière-plan, c'est pourquoi Service
, AsyncTask
, Threads
a été créé. Vous pouvez désactiver StrictMode
pour éviter un blocage mais jamais recommandé.
Je vous suggère de tirer parti de StrictMode
au moins en mode débogage. Utilisez le code ci-dessous pour obtenir les journaux de tout problème susceptible de ralentir votre application sur le fil principal.
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
Vous pouvez définir différentes pénalités -
penaltyLog() // to print log
penaltyDeath() // This will crash you App(so costly penalty)
penaltyDialog() // Show alert when something went lazy on Main thread
Il y a tellement de choses sur https://developer.Android.com/reference/Android/os/StrictMode.html
Si vous travaillez dans Kotlin et Anko, vous pouvez ajouter
doAsync {
method()
}