web-dev-qa-db-ja.com

Android:アクティビティコンテキスト外でのWebViewの使用

ユーザーの電話にビューを表示せずに定期的にWebサイトをスクレイピングするバックグラウンドIntentServiceを介してWebスクレイピングを実現しようとしています。

  • ロードされたページでjavascriptを呼び出さなければならないので、HttpGetなどは使用できません。
  • したがって、UIスレッドでのみ実行できるWebViewインスタンスを使用する必要があります。
  • WebViewを使用するアクティビティを開始しようとすると、ビューが電話のフォアグラウンドに入ります(Androidのアクティビティの設計に従って)
  • アクティビティコンテキストの外でWebViewを使用しようとすると、UI以外のスレッドでWebViewを使用できないというエラーが発生しました。
  • さまざまな複雑さの理由から、UIのない​​WebスクレイピングにRhinoなどのライブラリを使用することは検討できません。

この問題を回避する方法はありますか?

25
Pierre

私が間違っている場合は修正してください。この質問に対する正しい答えは、ユーザーがアクティビティによってユーザーを中断することなく電話で他のことを行っている間、バックグラウンドでWebViewを使用することが可能な方法はないということです。

RandyとCode_Yogaの両方の提案を適用しました。「Theme.NoDisplay」でアクティビティを使用して、WebViewでバックグラウンドサービスを起動し、いくつかの作業を行います。ただし、ビューが表示されていない場合でも、サービスを開始するためのその秒のアクティビティへの切り替えにより、ユーザーが中断されます(実行中の実行中のゲームを一時停止するなど)。

私のアプリの完全に悲惨なニュースなので、アクティビティを必要としないWebView(または同じことを実行できるWebViewの代わり)を使用する方法を誰かが私に与えてくれることを望んでいます

11
Pierre

サービスからWebビューを表示できます。以下のコードは、サービスがアクセスできるウィンドウを作成します。サイズが0 x 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);
    }
}

また、これにはAndroid.permission.SYSTEM_ALERT_WINDOW許可。

42
Randy

これを使用してアクティビティを非表示にすることができます

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

これを行うと、アプリでアクティビティが表示されなくなります。そして、あなたはアクティビティであなたの仕事をすることができます。

8
Code_Yoga

解決策はこのようなものでしたが、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
}

@JonasCzの助けを借りて: https://stackoverflow.com/a/28234761/46636

3
Shimon Doodkin

この問題を回避するために次のコードを使用しました。

Handler handler = new Handler(Looper.getMainLooper());
try
{
    handler.post(
        new Runnable()
        {
            @Override
            public void run()
            {
                ProcessRequest(); // Where this method runs the code you're needing
            }
        }
    );
} catch (Exception e)
{
    e.printStackTrace();
}
2
Graeme Campbell

WebViewはUIであるため、アクティビティまたはフラグメントの外部に存在できません。ただし、これは、アクティビティがWebViewの作成にのみ必要であり、そのすべてのリクエストを処理するわけではないことを意味します。

メインアクティビティで非表示のWebViewを作成し、静的コンテキストからアクセスできるようにすると、WebViewのすべてのIO =非同期で行われます。

そのグローバルアクセスの悩みを取り除くために、WebViewへの参照を使用してサービスを起動し、必要な作業を行うことができます。

1
user2587659

または、UIに読み込まれた情報を表示したくない場合は、同じ<===を実行できるWebViewの代わりに、HTTPを使用してURLを直接呼び出し、HTTPから返された応答を処理することができます

0
Nartus Team

1年半経ちますが、今は同じ問題に直面しています。 Nodeエンジン内で実行されているエンジンAndroid App。 JXCore と呼ばれています。また、WebViewなしでJavaScriptを実行する sample もご覧ください。本当に何を使用したのですか?

0
oriharel

スクレイピングを行うバックエンドサービスを作成してみませんか?

そして、RESTful Webサービスからの結果をポーリングするか、メッセージングミドルウェア(ZeroMQなど)を使用します。

ユースケースに合う場合は、おそらくよりエレガントになるでしょう。スクレイピングサービスにGCM経由でアプリプッシュメッセージを送信させます:)

0
McOzD

これが与えられた問題の特効薬かどうかはわかりません。 @Pierreの受け入れられた回答に従って(私には正しい音)

ユーザーがアクティビティによってユーザーを中断することなく、電話で他のことを行っている間、バックグラウンドでWebViewを使用する方法はありません。

したがって、この問題を解決するには、アーキテクチャ、フロー、戦略を変更する必要があると思います。

提案されたソリューション#1:サーバーからプッシュ通知を取得してバックグラウンドジョブを実行する代わりに、JSコードまたはWebViewを実行します。代わりに、ユーザーがアプリケーションを起動するたびに、バックエンドサーバーにクエリを実行して、廃棄を実行する必要があるかどうかを確認する必要があります。そして、バックエンド入力に基づいてAndroidクライアントはJSコードまたはWebViewを実行し、結果をサーバーに返すことができます。

私はこの解決策を試していません。しかし、それが実現可能であることを願っています。


これにより、コメントに記載されている次の問題も解決されます。

これは、バックエンドがボットとして同じIPからスクレイピングされ、ブロックされるためです(さまざまなページで大量のスクレイピングを行うために必要なバックエンドリソースに加えて)

しばらくの間、データが利用できない可能性があります(一部のユーザーがデータを破棄するまで)。しかし、確かにこの戦略を使用することで、エンドユーザーにより良いユーザーエクスペリエンスを提供できます。

0
Killer