Android:在Activity上下文之外使用WebView

时间:2013-09-18 06:11:12

标签: android android-activity android-webview web-scraping intentservice

我正在尝试通过后台IntentService实现Web Scraping,该后台定期抓取网站而不在用户手机上显示视图。

  • 由于我必须在加载的页面上调用一些javascript,我不能使用任何HttpGet等。
  • 因此,我必须使用只能在UI线程上运行的WebView实例。
  • 任何尝试启动使用WebView的Activity都会导致View进入手机前景(根据Android的活动设计)
  • 任何在Activity上下文之外使用WebView的尝试都会导致错误,指出您无法在非UI线程上使用WebView。
  • 出于各种复杂性原因,我不能考虑使用Rhino等库来进行无网页抓取。

有没有办法解决这个问题?

10 个答案:

答案 0 :(得分:41)

您可以从服务中显示网页浏览。下面的代码会创建一个您的服务可以访问的窗口。窗口不可见,因为大小为0乘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权限。

答案 1 :(得分:10)

如果我错了,请纠正我,但对这个问题的正确答案是,当用户在手机上做其他事情而没有通过活动中断用户时,没有办法在后台使用WebView。

我已经应用了Randy和Code_Yoga的建议:使用带有“Theme.NoDisplay”的活动来启动带有WebView的后台服务来完成一些工作。然而,即使没有可见的视图,在第二次启动服务时切换到该活动也会中断用户(例如暂停正在播放的正在运行的游戏)。

对我的应用程序来说完全是灾难性的消息,所以我仍然希望有人能给我一种方法来使用不需要Activity的WebView(或者可以替代可以实现同样功能的WebView)

答案 2 :(得分:7)

您可以使用此隐藏活动

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

执行此操作会阻止应用显示任何活动。 然后你可以在活动中完成你的工作。

答案 3 :(得分:3)

解决方案是这样的,但使用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/466363

答案 4 :(得分:1)

由于它是一个UI,WebView不能存在于Activity或Fragment之外。 但是,这意味着只需要一个Activity来创建WebView,而不是处理它的所有请求。

如果您在主活动中创建不可见的WebView并且可以从静态上下文访问它,那么您应该能够在任何地方在后台的视图中执行任务,因为我相信所有WebView的IO都是异步完成。

要取消全局访问权限,您可以随时启动一个引用WebView的服务来完成您需要的工作。

答案 5 :(得分:1)

我使用以下代码解决了这个问题:

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

答案 6 :(得分:0)

或者可以完成相同的WebView的替代&lt; ===如果您不希望在UI上显示加载的信息,也许您可​​以尝试使用HTTP直接调用url,并在返回时处理来自HTTP的响应

答案 7 :(得分:0)

我知道这已经过了一年半,但我现在面临同样的问题。我最终通过在我的Android应用程序中运行的Node引擎中运行我的Javascript代码来解决它。它被称为JXCore。你可以看看。另外,看一下在没有WebView的情况下运行Javascript的sample。我真的很想知道你最终使用了什么?

答案 8 :(得分:0)

为什么不创建一个为您进行抓取的后端服务?

然后您只是从RESTful Web服务轮询结果,甚至使用消息传递中间件(例如ZeroMQ)。

如果它适合您的使用案例,可能更优雅:让Scraping Service通过GCM发送您的应用推送消息:)

答案 9 :(得分:0)

我不确定这是否是解决特定问题的灵丹妙药。 按照@Pierre接受的答案(对我来说听起来正确)

  

没有一种方法可以在后台使用WebView,而   用户正在做手机上的其他事情而不会打扰用户   通过活动。

因此,我相信必须进行一些架构/流程/策略更改才能解决此问题。

建议的解决方案#1:而不是从服务器获取推送通知并运行后台作业,然后运行一些JS代码或WebView。取而代之的是,每当用户启动应用程序时,都应查询后端服务器,以了解是否需要执行任何剪贴。并且在后端输入的基础上,android客户端可以运行JS代码或WebView并将结果传递回服务器。

我还没有尝试过这种解决方案。但是希望这是可行的。


这还将解决注释中所述的以下问题:

之所以这样做,是因为后端将被检测为来自同一IP的机器人抓取程序并被阻止(除了后端资源需要在不同页面上进行大量抓取操作)。 < / p>

数据可能会在一段时间内不可用(直到某些用户为您删除数据)。但是可以肯定的是,我们可以使用这种策略为最终用户提供更好的用户体验。