web-dev-qa-db-fra.com

Android: Utilisation de WebView en dehors d'un contexte d'activité

J'essaie de réaliser le Web Scraping à travers un arrière-plan IntentService qui raccroche périodiquement un site Web sans afficher de vue sur le téléphone des utilisateurs. 

  • Comme je dois appeler du javascript sur la page chargée, je ne peux utiliser aucun HttpGet, etc.
  • Je dois donc utiliser une instance WebView qui ne peut s'exécuter que sur un thread d'interface utilisateur.
  • Toute tentative de démarrage d'une activité utilisant une vue Web entraîne l'affichage d'une vue au premier plan des téléphones (conformément à la conception des activités Android).
  • Toute tentative d'utilisation d'une WebView en dehors d'un contexte d'activité entraînait une erreur indiquant que vous ne pouviez pas utiliser WebView sur un thread non-UI.
  • Pour diverses raisons de complexité, je ne peux pas envisager d'utiliser des bibliothèques telles que Rhino pour le raclage Web sans interface utilisateur.

Existe-t-il un moyen de contourner ce problème?

21
Pierre

Corrigez-moi si je me trompe, mais la réponse correcte à cette question est qu'il n'y a AUCUN moyen possible d'utiliser une WebView en arrière-plan lorsque l'utilisateur fait autre chose au téléphone sans l'interrompre au moyen d'une activité.

J'ai appliqué les suggestions de Randy et de Code_Yoga: Utiliser une activité avec "Theme.NoDisplay" pour lancer un service d'arrière-plan avec WebView pour effectuer un travail. Cependant, même si aucune vue n'est visible, le passage à cette activité pendant cette seconde pour que les services démarrent, interrompt l'utilisateur (par exemple, il met en pause un jeu en cours).

Des nouvelles totalement désastreuses pour mon application, j'espère donc toujours que quelqu'un me donnera le moyen d'utiliser une WebView qui ne nécessite pas d'activité (ou un substitut d'une WebView pouvant accomplir la même chose)

9
Pierre

Vous pouvez afficher une vue Web à partir d'un service. Le code ci-dessous crée une fenêtre à laquelle votre service a accès. La fenêtre n'est pas visible car la taille est 0 par 0.

public class ServiceWithWebView extends Service {

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

        WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT);
        params.gravity = Gravity.TOP | Gravity.LEFT;
        params.x = 0;
        params.y = 0;
        params.width = 0;
        params.height = 0;

        LinearLayout view = new LinearLayout(this);
        view.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT));

        WebView wv = new WebView(this);
        wv.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
        view.addView(wv);
        wv.loadUrl("http://google.com");

        windowManager.addView(view, params);
    }
}

De plus, cela nécessitera l'autorisation Android.permission.SYSTEM_ALERT_WINDOW.

39
Randy

Vous pouvez l'utiliser pour masquer l'activité 

         <activity Android:name="MyActivity"
          Android:label="@string/app_name"
          Android:theme="@Android:style/Theme.NoDisplay">

Cela empêchera l'application d'afficher une activité. Et vous pourrez ensuite faire votre travail dans l'activité.

7
Code_Yoga

la solution était comme ça, mais avec Looper.getMainLooper ():

https://github.com/JonasCz/save-for-offline/blob/master/app/src/main/Java/jonas/tool/saveForOffline/ScreenshotService.Java

@Override
public void onCreate() {
    super.onCreate();
    //HandlerThread thread = new HandlerThread("ScreenshotService", Process.THREAD_PRIORITY_BACKGROUND);
    //thread.start();
    //mServiceHandler = new ServiceHandler(thread.getLooper()); // not working
    mServiceHandler = new ServiceHandler(Looper.getMainLooper()); // working
}

avec l'aide de @JonasCz: https://stackoverflow.com/a/28234761/466363

2
Shimon Doodkin

Une vue Web ne peut pas exister en dehors d'une activité ou d'un fragment, car il s'agit d'une interface utilisateur . Cependant, cela signifie qu'une activité n'est nécessaire que pour créer la vue Web et ne pas traiter toutes ses demandes.

Si vous créez la WebView invisible dans votre activité principale et que vous la rendez accessible à partir d'un contexte statique, vous devriez pouvoir effectuer des tâches dans la vue en arrière-plan depuis n'importe où, car je pense que tous les IO de WebView sont effectués de manière asynchrone. 

Pour vous prémunir de cet accès mondial, vous pouvez toujours lancer un service avec une référence à WebView pour effectuer le travail dont vous avez besoin.

1
user2587659

ou un substitut d'une WebView qui peut accomplir la même chose <=== si vous ne souhaitez pas afficher les informations chargées sur l'interface utilisateur, vous pouvez peut-être essayer d'utiliser HTTP pour appeler directement l'URL et traiter la réponse renvoyée par HTTP.

0
Nartus Team

Je sais que cela fait un an et demi, mais je suis maintenant confronté au même problème. Je l'ai finalement résolu en exécutant mon code Javascript dans un moteur de nœud qui s'exécute dans mon application Android. Cela s'appelle JXCore . Vous pouvez jeter un coup d'oeil. Jetez également un coup d’œil à ce sample qui exécute Javascript sans WebView. Je voudrais vraiment savoir ce que vous avez fini par utiliser?

0
oriharel

Pourquoi ne créez-vous pas un service d’arrière-plan qui effectue le nettoyage à votre place?

Et ensuite, vous n'interrogez que les résultats d'un service Web RESTful ou utilisez même un middleware de messagerie (par exemple, ZeroMQ).

Peut-être plus élégant si cela correspond à votre cas d'utilisation: laissez le service Scraping envoyer vos messages Push App via GCM :)

0
McOzD