ユーザーの電話にビューを表示せずに定期的にWebサイトをスクレイピングするバックグラウンドIntentServiceを介してWebスクレイピングを実現しようとしています。
この問題を回避する方法はありますか?
私が間違っている場合は修正してください。この質問に対する正しい答えは、ユーザーがアクティビティによってユーザーを中断することなく電話で他のことを行っている間、バックグラウンドでWebViewを使用することが可能な方法はないということです。
RandyとCode_Yogaの両方の提案を適用しました。「Theme.NoDisplay」でアクティビティを使用して、WebViewでバックグラウンドサービスを起動し、いくつかの作業を行います。ただし、ビューが表示されていない場合でも、サービスを開始するためのその秒のアクティビティへの切り替えにより、ユーザーが中断されます(実行中の実行中のゲームを一時停止するなど)。
私のアプリの完全に悲惨なニュースなので、アクティビティを必要としないWebView(または同じことを実行できるWebViewの代わり)を使用する方法を誰かが私に与えてくれることを望んでいます
サービスから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
許可。
これを使用してアクティビティを非表示にすることができます
<activity Android:name="MyActivity"
Android:label="@string/app_name"
Android:theme="@Android:style/Theme.NoDisplay">
これを行うと、アプリでアクティビティが表示されなくなります。そして、あなたはアクティビティであなたの仕事をすることができます。
解決策はこのようなものでしたが、Looper.getMainLooper()を使用しました:
@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
この問題を回避するために次のコードを使用しました。
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();
}
WebViewはUIであるため、アクティビティまたはフラグメントの外部に存在できません。ただし、これは、アクティビティがWebViewの作成にのみ必要であり、そのすべてのリクエストを処理するわけではないことを意味します。
メインアクティビティで非表示のWebViewを作成し、静的コンテキストからアクセスできるようにすると、WebViewのすべてのIO =非同期で行われます。
そのグローバルアクセスの悩みを取り除くために、WebViewへの参照を使用してサービスを起動し、必要な作業を行うことができます。
または、UIに読み込まれた情報を表示したくない場合は、同じ<===を実行できるWebViewの代わりに、HTTPを使用してURLを直接呼び出し、HTTPから返された応答を処理することができます
スクレイピングを行うバックエンドサービスを作成してみませんか?
そして、RESTful Webサービスからの結果をポーリングするか、メッセージングミドルウェア(ZeroMQなど)を使用します。
ユースケースに合う場合は、おそらくよりエレガントになるでしょう。スクレイピングサービスにGCM経由でアプリプッシュメッセージを送信させます:)
これが与えられた問題の特効薬かどうかはわかりません。 @Pierreの受け入れられた回答に従って(私には正しい音)
ユーザーがアクティビティによってユーザーを中断することなく、電話で他のことを行っている間、バックグラウンドでWebViewを使用する方法はありません。
したがって、この問題を解決するには、アーキテクチャ、フロー、戦略を変更する必要があると思います。
提案されたソリューション#1:サーバーからプッシュ通知を取得してバックグラウンドジョブを実行する代わりに、JSコードまたはWebViewを実行します。代わりに、ユーザーがアプリケーションを起動するたびに、バックエンドサーバーにクエリを実行して、廃棄を実行する必要があるかどうかを確認する必要があります。そして、バックエンド入力に基づいてAndroidクライアントはJSコードまたはWebViewを実行し、結果をサーバーに返すことができます。
私はこの解決策を試していません。しかし、それが実現可能であることを願っています。
これにより、コメントに記載されている次の問題も解決されます。
これは、バックエンドがボットとして同じIPからスクレイピングされ、ブロックされるためです(さまざまなページで大量のスクレイピングを行うために必要なバックエンドリソースに加えて)
しばらくの間、データが利用できない可能性があります(一部のユーザーがデータを破棄するまで)。しかし、確かにこの戦略を使用することで、エンドユーザーにより良いユーザーエクスペリエンスを提供できます。