Если вы работаете в интернет-магазине, как мой работодатель Capriza, вы, вероятно, хорошо себя чувствуете в последнее десятилетие. Javascript стал основным языком для большинства разработчиков. Имея более мощные, чем когда-либо ранее устройства на стороне клиента, и порты на стороне сервера, довольно легко понять, в какую технологию вы должны инвестировать. Мне очень нравится, что Джефф Этвуд сформулировал это в виде закона:

Любое приложение, которое может быть написано на JavaScript, будет в конечном итоге написано на JavaScript.

Благодаря своей кроссплатформенной природе, мы также видим, что он становится нативным для мобильных устройств с растущим успехом React Native. Однако самая сильная сторона React Native - это возможность разработчикам писать Javascript для рендеринга собственных виджетов, в то время как мобильная разработка - это намного больше, чем пользовательский интерфейс.

При создании мобильного приложения в 2017 году нужно думать не только о взаимодействии с пользователем в Интернете. Необходимо учитывать контекстно-зависимое программное обеспечение, чтобы конкурировать с перегруженными магазинами приложений. Под «контекстно-зависимым» я подразумеваю программное обеспечение, работающее в фоновом режиме, чтобы когда-либо опрашивать контекст пользователя (местоположение, время суток, новые данные, полученные из серверной части), что не позволяет пользователю регистрироваться в приложении. вместо этого приложение связывается с пользователем.

Но что, если все ваше программное обеспечение уже написано на Javascript, и вы хотите запускать его на Android в фоновом режиме? В Android SDK Javascript работает в WebView. WebViews запускается в потоке пользовательского интерфейса, а фоновая служба запрещена в потоке пользовательского интерфейса. Вы можете включить в свое приложение механизм выполнения Javascript, но это резко увеличит размер вашего приложения и потребует огромных усилий для ваших исследований и разработок.

Вход в WindowManager SDK. Этот компонент представляет собой ViewManager, отвечающий за дисплей телефона, который всегда активен. даже когда ваше приложение неактивно. Таким образом, при запуске службы получить указатель на нее так же просто, как вызвать Context.getSystemService (Context.WINDOW_SERVICE).

Добавление / удаление представлений в WindowManager возможно из всех потоков и не обеспечивает выполнение требования runOnUiThread. С разрешением android.permission.SYSTEM_ALERT_WINDOW ваше приложение теперь может добавлять представления в WindowManager, включая WebView. Все, что вам нужно сделать, это назвать свой URL и vualá! у вас в фоновом режиме работает JavaScript.

Вот фрагмент кода:

...

public class BackgroundService extends Service {

    ...

    @Override
    public int onStartCommand(Intent intent, int flags, int startId{

        
        final WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
                android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
                android.view.ViewGroup.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                      | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
                PixelFormat.TRANSLUCENT
        );

        params.gravity = Gravity.TOP | Gravity.START;
        params.x = 0;
        params.y = 0;
        params.width = 0;
        params.height = 0;

        final WebView wv = new WebView(this);

        wv.setWebViewClient(new WebViewClient() {

            @Override
            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
                Log.d("Error","loading web view: request: "+request+" error: "+error);
            }

            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {

                
                if (request.getUrl().toString().contains("/endProcess")) {
                    
                    windowManager.removeView(wv);

                    wv.post(new Runnable() {
                        @Override
                        public void run() {
                            wv.destroy();
                        }
                    });
                    stopSelf();
                    return new WebResourceResponse("bgsType", "someEncoding", null);
                }
                else {
                    return null;
                }
            }
        });
        wv.loadUrl(bgsUrl);
        windowManager.addView(wv, params);
    }

    ...
}

Этот код работает в сервисном компоненте Android. Получив указатель на WindowManager от системной службы, мы объявляем его параметры макета. В нашем случае нет видимого пользовательского интерфейса - мы просто хотим запустить наш Javascript в фоновом режиме - поэтому мы объявляем его не фокусируемым или осязаемым, а также присваиваем ему нулевую ширину и высоту.

Затем мы создаем WebView. Мы также можем добавить WebViewClient для перехвата вызовов URL - это хороший способ связи между Javascript и нативным (это также можно сделать, применив интерфейс Javascript и вызвав метод WebView addJavascriptInterface).

Затем мы вызываем loadUrl и метод addView WindowManager. теперь URL-адрес может указывать на некоторый HTML, который загружает Javascript и выполняет любую логику. Важно, чтобы как только сторона Javascript была завершена, она сообщила об этом WebViewClient (например, вызвав некоторый специальный URL-адрес - в нашем случае / endProcess), который, в свою очередь, завершит Служба Android (путем вызова stopSelf).

Однако несколько важных замечаний:

Как уже упоминалось, этот поток использует очень специальное разрешение android.permission.SYSTEM_ALERT_WINDOW, с которым Google очень осторожен. Это разрешение позволяет вашему приложению использовать другое приложение. Вы можете увидеть это на странице специальных разрешений в настройках (см. Снимок экрана ниже)

Рекламные сети и вредоносные приложения могут злоупотреблять этим разрешением для перехвата кликов и отображения наложения. Поэтому вам нужно быть в курсе последних обновлений Android (например, в Android O вам нужно будет добавить TYPE_APPLICATION_PANEL вместо TYPE_PHONE в параметры макета WindowManager. ).

Вы также должны быть осторожны, чтобы не разряжать батарею пользователя, и убедитесь, что Javascript выполняет свою задачу очень быстро и быстро вызывает stopSelf.