Статьи

Изучение Android: замораживание интерфейса с BroadcastReceiver

Как я упоминал в предыдущем посте, я недавно написал некоторый код в своем приложении для Android, чтобы информировать BroadcastReceiver, когда служба обрабатывает твит со ссылкой в ​​нем, но при реализации этого мне удавалось блокировать пользовательский интерфейс каждый раз, когда это происходило.

Я совершил глупую (задним числом) ошибку, не осознав, что не должен выполнять большую логику в BroadcastReceiver.onReceive, поскольку этот бит кода выполняется в потоке пользовательского интерфейса.

Сервисный код, который вызывает широковещательное сообщение, такой же, как в предыдущем посте:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public class TweetService extends IntentService {
    ...
    @Override
    protected void onHandleIntent(Intent intent) {
        StatusListener listener = new UserStreamListener() {
           // override a whole load of methods - removed for brevity
  
            public void onStatus(Status status) {
                String theTweet = status.getText();
                if (status.getText().contains("http://")) {
                    Intent tweetMessage = new Intent(TweetTask.NEW_TWEET);
                    tweetMessage.putExtra(android.content.Intent.EXTRA_TEXT, status.getText());
                    sendBroadcast(tweetMessage);
                }
  
            }
        };
  
        // code to connect to the twitter streaming API
    }
}

Это тогда обрабатывается как BroadcastReceiver:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MyActivity extends Activity {
    protected void onPause() {
        super.onPause();
        if (dataUpdateReceiver != null) unregisterReceiver(dataUpdateReceiver);
    }
  
    protected void onResume() {
        super.onResume();
        if (dataUpdateReceiver == null) dataUpdateReceiver = new DataUpdateReceiver();
        IntentFilter intentFilter = new IntentFilter(TweetTask.NEW_TWEET);
        registerReceiver(dataUpdateReceiver, intentFilter);
    }
  
    private class DataUpdateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(TweetTask.NEW_TWEET)) {
                Pattern p = Pattern.compile("(http://[^\\s]+)");
                String theTweet = intent.getStringExtra(TweetTask.NEW_TWEET);
                Matcher matcher = p.matcher(theTweet);
  
                int startIndex = -1;
                int endIndex = -1;
                while (matcher.find()) {
                    startIndex = matcher.start();
                    endIndex = matcher.end();
                }
  
                if (startIndex != -1 && endIndex != -1) {
                    String resolvedUrl = resolveUrl(theTweet.substring(startIndex, endIndex));
                    saveToDatabase(resolvedUrl);
                    updateUI(resolvedUrl);
                }
            }
        }
    }
}

В частности, строка ‘resolUrl’, вероятно, была единственной, вызывающей проблему, так как она делает сетевой вызов для разрешения URL-адресов от укороченных ссылок .

Чтобы остановить зависание экрана, мне просто нужно было переместить большую часть кода из BroadcastReceiver в TweetService:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class TweetService extends IntentService {
    ...
    @Override
    protected void onHandleIntent(Intent intent) {
        StatusListener listener = new UserStreamListener() {
           // override a whole load of methods - removed for brevity
  
            public void onStatus(Status status) {
                String theTweet = status.getText();
                if (status.getText().contains("http://")) {
                    Pattern p = Pattern.compile("(http://[^\\s]+)");
                    Matcher matcher = p.matcher(theTweet);
  
                    int startIndex = -1;
                    int endIndex = -1;
                    while (matcher.find()) {
                        startIndex = matcher.start();
                        endIndex = matcher.end();
                    }
  
                    if (startIndex != -1 && endIndex != -1) {
                        String resolvedUrl = resolveUrl(theTweet.substring(startIndex, endIndex));
                        saveToDatabase(resolvedUrl);
  
                        Intent tweetMessage = new Intent(TweetTask.NEW_TWEET);
                        tweetMessage.putExtra(android.content.Intent.EXTRA_TEXT, resolvedUrl);
                        sendBroadcast(tweetMessage);
                    }
                }
            }
        };
  
        // code to connect to the twitter streaming API
    }
}

И тогда код для BroadcastReceiver становится намного проще, что означает, что мы делаем меньше работы над потоком пользовательского интерфейса:

1
2
3
4
5
6
7
8
9
private class DataUpdateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(TweetTask.NEW_TWEET)) {
                String url = intent.getStringExtra(TweetTask.NEW_TWEET);
                updateUI(url);
            }
        }
    }

И замораживание интерфейса прошло!

Ссылка: Изучение Android: замораживание пользовательского интерфейса с BroadcastReceiver от нашего партнера JCG Марка Нидхэма в блоге Марка Нидхэма .