Статьи

Взаимодействие Salesforce с Android

В этой статье мы собираемся исследовать создание простого нативного приложения для Android, которое использует Chatter REST API в рамках платформы Salesforce. Для этого мы будем использовать Salesforce Mobile SDK 2.1 , который действует как оболочка для низкоуровневых функций HTTP, что позволяет нам легко обрабатывать OAuth и последующие вызовы API REST. TemplateApp, предоставляемый в SDK в качестве основы, действительно станет вашей самой чистой отправной точкой. Мой учебник по существу использует структуру TemplateApp и опирается на нее, заимствуя и изменяя из примера приложения REST Explorer; это помогает гарантировать, что вещи максимально просты. Мы не собираемся затрагивать все аспекты создания этого приложения, но вместо этого рассмотрим основные моменты, давая читателю хорошую отправную точку и пытаясь расширить документацию salesforce.com. Этот учебник пытается служить своего рода шуткой для разработчиков, не слишком знакомых с платформой для использования API таким способом, который, по-видимому, более знаком. Многое из того, что мы рассмотрим, дополнит Руководство разработчика Salesforce Mobile SDK ; в этом руководстве я буду ссылаться на соответствующие номера страниц этого документа, а не воспроизводить эту информацию здесь в полном объеме.

Начало настройки

Я использую IntelliJ IDEA для этого урока, это IDE, на котором основана Android Studio. Если вы уже используете Android Studio, в процессе работы заметных отличий не будет; Пользователи Eclipse готовы к работе. После настройки IDE мы можем приступить к установке Salesforce Mobile SDK 2.1 (см. Ссылку в параграфе выше). Salesforce.com рекомендует установку на основе Node.js с использованием диспетчера пакетов узла. Мы пойдем альтернативным маршрутом; вместо этого мы собираемся клонировать репозиторий из Github [Страница 16].

После настройки базовой среды перейдите по адресу https://developer.salesforce.com/signup и зарегистрируйте свою учетную запись Developer Edition (DE). В этом примере я рекомендую подписаться на Developer Edition, даже если у вас уже есть учетная запись. Это гарантирует, что вы получите чистую среду с последними включенными функциями. Затем перейдите на http://login.salesforce.com, чтобы войти в свою учетную запись разработчика.

После завершения регистрации следуйте инструкциям в Руководстве по Mobile SDK для создания подключенного приложения [Страница 13]. Для целей данного урока вам нужно всего лишь заполнить обязательные поля.

1

URL-адрес обратного вызова, предоставленный для OAuth, не обязательно должен быть действительным; это только должно соответствовать тому, что приложение ожидает в этой области. Вы можете использовать любой пользовательский префикс, такой как sfdc: //.

Важное замечание : Для нативного приложения вы ДОЛЖНЫ указать « Выполнять запросы от вашего имени в любое время (refresh_token)» в выбранных вами областях OAuth, иначе сервер отклонит вас, и никто не любит отклонение. В этом разделе см. «Руководство по мобильному SDK», более подробную информацию см. По адресу : [ http://github.com/forcedotcom/SalesforceMobileSDK-iOS/issues/211#issuecomment-23953366 ]

Когда вы закончите, вам должна быть показана страница, которая содержит ваш Consumer Key и Secret среди прочего.

2

Теперь, когда мы позаботились о вещах на стороне сервера, давайте переключим наше внимание на настройку нашего телефонного приложения.

Во-первых, мы собираемся начать новый проект в IntelliJ; убедитесь, что вы выбрали Application Module, а не Gradle: Android Application Module , так как способ структурирования проекта не очень подходит для системы сборки Gradle.

3

Назовите его как хотите, не забудьте снять флажок «Привет, мир!». Активность , так как нам это не понадобится. Теперь, когда вы создали свой проект, перейдите в File -> Import Module…

Перейдите в каталог, в котором вы клонировали репозиторий Mobile SDK, раскройте собственный каталог, и вы увидите проект с именем «SalesforceSDK» с логотипом IntelliJ рядом с ним.

4

Выберите его и нажмите ОК. На следующем экране убедитесь, что выбран параметр импорта из внешней модели и что элемент списка Eclipse выделен. Нажмите «Далее», а затем снова нажмите «Далее» на следующем экране, не внося никаких изменений. Когда вы дойдете до последнего экрана, установите флажок рядом с SalesforceSDK и нажмите «Готово». IntelliJ теперь импортирует проект Eclipse (Salesforce SDK) в ваш проект в виде модуля.

Salesforce Mobile SDK теперь ваша команда… .almost; перейдите в File -> Project Structure … Выберите «Facets» в «Project Settings», теперь выберите тот, который имеет Salesforce SDK в скобках; убедитесь, что в поле « Модуль библиотеки» установлен флажок [IMG]. Теперь выберите другой, затем выберите вкладку «Упаковка» и убедитесь, что флажок « Включить слияние манифеста» установлен.

5

Затем выберите «Модули» в списке «Настройки проекта», затем выберите модуль SalesforceSDK. Под вкладкой зависимостей должен быть элемент с красным текстом; щелкните по нему правой кнопкой мыши и удалите его. Оттуда нажмите < имя вашего модуля >; на вкладке зависимостей нажмите зеленый «+», выберите «Module Dependency…», Salesforce SDK должен быть единственным вариантом, нажмите «Ok». Теперь выберите «Применить» в окне «Структура проекта» и нажмите «ОК».

6

Звонить

Создайте файл с именем bootconfig.xml в res / values ​​/; содержимое этого файла должно быть следующим:

01
02
03
04
05
06
07
08
09
10
<?xml version="1.0" encoding="utf-8"?>
 
<resources>
    <string name="remoteAccessConsumerKey"> YOUR CONSUMER KEY </string>
    <string name="oauthRedirectURI"> YOUR REDIRECT URI </string>
    <string-array name="oauthScopes">
        <item>chatter_api</item>
    </string-array>
    <string name="androidPushNotificationClientId"></string>
</resources>

Помните подключенное приложение, которое мы создали ранее? Здесь вы найдете ключ потребителя и URI перенаправления (обратного вызова).

Для любопытных, несмотря на то, что мы указали refresh_token в нашей серверной области OAuth Scopes, нам не нужно определять его здесь. Причина этого заключается в том, что эта область всегда требуется для доступа к платформе из собственного приложения, поэтому Mobile SDK включает ее автоматически.

Затем убедитесь, что ваш файл String.xml выглядит примерно так:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="account_type">com.salesforce.samples.templateapp.login</string>
    <string name="app_name"><b>Template</b></string>
    <string name="app_package">com.salesforce.samples.templateapp</string>
        <string name="api_version">v30.0</string>
</resources>

Вышеуказанные значения должны быть уникальными для вашего приложения.

Теперь создайте другой класс с именем KeyImpl.

1
2
3
4
5
6
7
public class KeyImpl implements KeyInterface {
 
    @Override
    public String getKey(String name) {
        return Encryptor.hash(name + "12s9adpahk;n12-97sdainkasd=012", name + "12kl0dsakj4-cxh1qewkjasdol8");
    }
}

После этого создайте произвольное действие с соответствующим макетом, расширяющим SalesforceActivity, и заполните его следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
public class TutorialApp extends Application {
 
    @Override
    public void onCreate() {
        super.onCreate();
        SalesforceSDKManager.initNative(getApplicationContext(), new KeyImpl(), TutorialActivity.class);
 
        /*
         * Un-comment the line below to enable push notifications in this app.
         * Replace 'pnInterface' with your implementation of 'PushNotificationInterface'.
         * Add your Google package ID in 'bootonfig.xml', as the value
         * for the key 'androidPushNotificationClientId'.
         */
        // SalesforceSDKManager.getInstance().setPushNotificationReceiver(pnInterface);
    }
}

Это точка входа в наше приложение, где мы инициализируем SalesforceSDKManager.

Из Руководства разработчика по Salesforce Mobile SDK:

« Класс SalesforceSDKManager верхнего уровня реализует функциональность паролей для приложений, которые используют пароли, и заполняет пробелы для тех, которые этого не делают. Он также устанавливает условия для входа в систему, очищает после выхода из системы и предоставляет специальный наблюдатель событий, который информирует ваше приложение об удалении учетной записи системного уровня. Протоколы OAuth обрабатываются автоматически с помощью внутренних классов ».

Для этого урока наш соответствующий макет выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical" android:layout_width="match_parent"
              android:layout_height="match_parent" android:background="#454545"
              android:id="@+id/root">
 
<TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Large Text"
        android:id="@+id/data_display"/>
 
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Fetch Data"
        android:id="@+id/button"
        android:layout_gravity="bottom"
        android:onClick="onFetchClick"/>
</LinearLayout>

Как вы можете видеть выше, для целей этого урока мы делаем вещи по-настоящему простыми. Кнопка для вызова и текстовое поле для его отображения.

Прежде чем мы перейдем к деталям реализации, я хочу упомянуть критическую точку в файле манифеста.

Поскольку у нас есть новая точка входа в приложение, в нашем случае TutorialApp , мы должны сообщить системе об этом, определив это в манифесте.

7

Если вы пропустите этот шаг, ваше приложение будет аварийно завершено с ошибкой во время выполнения, сообщающей, что вы не сделали соответствующий вызов SalesforceSDKManager.init ()

После этого ваше приложение должно работать без проблем.

Теперь мы собираемся написать функции для выполнения фактического запроса. Следующие методы будут включены в действие, созданное выше.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private void sendRequest(RestRequest restRequest) {
        client.sendAsync(restRequest, new RestClient.AsyncRequestCallback() {
 
            @Override
            public void onSuccess(RestRequest request, RestResponse result) {
                try {
 
                    //Do something with JSON result.
                    println(result);  //Use our helper function, to print our JSON response.
 
                } catch (Exception e) {
                    e.printStackTrace();
                }
 
                EventsObservable.get().notifyEvent(EventsObservable.EventType.RenditionComplete);
            }
 
            @Override
            public void onError(Exception e) {
                e.printStackTrace();
                EventsObservable.get().notifyEvent(EventsObservable.EventType.RenditionComplete);
            }
        });
    }

Приведенный выше код выполняет объект RestRequest, который мы передаем ему, и возвращает результаты асинхронно.

Давайте создадим объект RestRequest для выполнения клиентом:

Во-первых, нам нужна пара вспомогательных методов, чтобы помочь в создании наших HttpEntities

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
38
private Map<String, Object> parseFieldMap(String jsonText) {
        String fieldsString = jsonText;
        if (fieldsString.length() == 0) {
            return null;
        }
 
        try {
            JSONObject fieldsJson = new JSONObject(fieldsString);
            Map<String, Object> fields = new HashMap<String, Object>();
            JSONArray names = fieldsJson.names();
            for (int i = 0; i < names.length(); i++) {
                String name = (String) names.get(i);
                fields.put(name, fieldsJson.get(name));
            }
            return fields;
 
        } catch (Exception e) {
            Log.e("ERROR", "Could not build request");
            e.printStackTrace();
            return null;
        }
    }
 
 
 
    private HttpEntity getParamsEntity(String requestParamsText)
            throws UnsupportedEncodingException {
        Map<String, Object> params = parseFieldMap(requestParamsText);
        if (params == null) {
            params = new HashMap<String, Object>();
        }
        List<NameValuePair> paramsList = new ArrayList<NameValuePair>();
        for (Map.Entry<String, Object> param : params.entrySet()) {
            paramsList.add(new BasicNameValuePair(param.getKey(),
                    (String) param.getValue()));
        }
        return new UrlEncodedFormEntity(paramsList);
    }

Далее мы определяем метод, который будет генерировать желаемый объект RestRequest .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
private RestRequest generateRequest(String httpMethod, String resource, String jsonPayloadString) {
        RestRequest request = null;
 
        if (jsonPayloadString == null) {
            jsonPayloadString = "";
        }
        String url = String.format("/services/data/%s/" + resource, getString(R.string.api_version)); // The IDE might highlight this line as having an error. This is a bug, the code will compile just fine.
        try {
            HttpEntity paramsEntity = getParamsEntity(jsonPayloadString);
            RestRequest.RestMethod method = RestRequest.RestMethod.valueOf(httpMethod.toUpperCase());
            request = new RestRequest(method, url, paramsEntity);
            return request;
        } catch (UnsupportedEncodingException e) {
            Log.e("ERROR", "Could not build request");
            e.printStackTrace();
        }
        return request;
    }

Наконец, вспомогательный метод, который печатает наш результат. Это не является строго необходимым, но включено здесь для

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
/**
 * Helper method to print object in the result_text field
 *
 * @param object
 */
 
    private void println(Object object) {
        if (resultText == null)
            return;
 
        StringBuffer sb = new StringBuffer(resultText.getText());
        String text;
        if (object == null) {
            text = "null";
        } else {
            text = object.toString();
        }
        sb.append(text).append("\n");
        resultText.setText(sb);
    }

Использование:

Помните ту кнопку, которую мы создали ранее в нашем макете? Теперь мы собираемся дать ему немного жизни.

1
2
3
4
public void onFetchClick(View view) {
        RestRequest feedRequest = generateRequest("GET", "chatter/feeds/news/me/feed-items", null);
        sendRequest(feedRequest);
    }

Теперь запустите ваше приложение, и, если все пойдет хорошо, вас попросят ввести учетные данные Salesforce. Затем вас попросят авторизовать приложение, которое вы только что создали; нажмите кнопку « Разрешить», и вы увидите свое приложение.

Когда вы нажимаете кнопку, она должна сбросить все данные JSON из нашего запроса в наше текстовое поле. Это не красиво, но это делает работу и иллюстрирует концепции.

Теперь вы можете взять эти данные, привязать их к ListView или использовать их для выполнения других запросов, таких как «лайк» или «комментарий».

Вот полностью рабочий пример приложения для справки:

Замечания:

Я бы порекомендовал использовать библиотеку для разбора JSON, такую ​​как Gson или Jackson. Мы не делали этого здесь для понимания и преемственности, поскольку это ближе ко всему, что вы увидите в документации и примерах Salesforce.

Ресурсы

Обратитесь к этим документам, чтобы увидеть многое из того, что вы можете сделать.

Повеселись!