安卓应用开发秘籍第二版-六-

139 阅读49分钟

安卓应用开发秘籍第二版(六)

原文:zh.annas-archive.org/md5/ceefdd89e585c59c20db6a7760dc11f1

译者:飞龙

协议:CC BY-NC-SA 4.0

第十三章:获取位置和使用地理围栏

在本章中,我们将涵盖以下主题:

  • 如何获取最后一次位置

  • 解决使用 GoogleApiClient OnConnectionFailedListener 报告的问题

  • 如何接收位置更新

  • 创建并监控地理围栏

引言

位置感知为应用带来了许多好处,实际上,好处如此之多,以至于现在连桌面应用也尝试获取用户的位置信息。位置信息的用途包括逐向导航、"查找最近"的应用程序、基于位置的提醒,以及现在甚至有了让你用设备去探索的基于位置的游戏。

Google API 提供了丰富的功能,用于创建具有位置感知的应用程序和地图功能。我们的第一个方法如何获取最后一次位置将查看设备上存储的最后一次已知位置。如果你的应用不是位置密集型的,这可能提供了一种获取用户位置而不需要大量资源开销的理想方式。如果你需要持续更新,那么请转向如何接收位置更新的方法。尽管持续的位置更新需要更多资源,但用户可能会理解你为他们提供逐向导航时的情况。如果你请求位置更新是为了邻近位置,请查看使用地理围栏选项,在创建和监控地理围栏的方法中。

本章中的所有方法都使用 Google 库。如果你还没有下载 SDK 包,请按照 Google 的说明操作。

提示

developer.android.com/sdk/installing/adding-packages.html添加 SDK 包。

现在你已经获得了位置信息,你很可能会想要将其映射出来。这也是 Google 在 Android 上使用 Google Maps API 使操作变得非常简单的另一个领域。要开始使用 Google Maps,请在 Android Studio 中创建新项目时查看Google Maps Activity选项。与我们在这些方法中通常选择的空白活动不同,请选择Google Maps Activity,如下截图所示:

介绍

如何获取最后一次位置

我们将从这一章开始介绍一个常用的简单方法:如何获取最后一次已知位置。这是一种使用 API 而几乎不消耗资源的方法。(这意味着,你的应用不会耗尽电池电量。)

本方法还提供了设置 Google 位置 API 的良好介绍。

准备就绪

在 Android Studio 中创建一个新项目,并将其命名为:GetLastLocation。使用默认的手机 & 平板选项,在选择活动类型时,选择空活动

如何操作...

首先,我们将在 Android Manifest 中添加必要的权限,然后创建一个带有ButtonTextView元素的布局。最后,我们将创建一个GoogleAPIClient API 来访问最后一次位置。打开 Android Manifest 并按照以下步骤操作:

  1. 添加以下权限:

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    
  2. Gradle Scripts部分下,打开**build.gradle (Module: app)**文件,如下截图所示:如何操作...

  3. dependencies部分添加以下声明:

    compile 'com.google.android.gms:play-services:8.4.0'
    
  4. 打开activity_main.xml文件,用以下 XML 替换现有的TextView

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Get Location"
        android:layout_centerInParent="true"
        android:onClick="getLocation"/>
    
  5. 打开MainActivity.java文件,并添加以下全局变量:

    GoogleApiClient mGoogleApiClient;
    TextView mTextView;
    Button mButton;
    
  6. 添加ConnectionCallbacks的类:

    GoogleApiClient.ConnectionCallbacks mConnectionCallbacks = new GoogleApiClient.ConnectionCallbacks() {
        @Override
        public void onConnected(Bundle bundle) {
            mButton.setEnabled(true);
        }
        @Override
        public void onConnectionSuspended(int i) {}
    };
    
  7. 添加处理OnConnectionFailedListener回调的类:

    GoogleApiClient.OnConnectionFailedListener mOnConnectionFailedListener = new GoogleApiClient.OnConnectionFailedListener() {
        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {
            Toast.makeText(MainActivity.this, connectionResult.toString(), Toast.LENGTH_LONG).show();
        }
    };
    
  8. 将以下代码添加到现有的onCreate()方法中:

    mTextView = (TextView) findViewById(R.id.textView);
    mButton = (Button) findViewById(R.id.button);
    mButton.setEnabled(false);
    setupGoogleApiClient();
    
  9. 添加设置GoogleAPIClient的方法:

    protected synchronized void setupGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addConnectionCallbacks(mConnectionCallbacks)
            .addOnConnectionFailedListener(mOnConnectionFailedListener)
            .addApi(LocationServices.API)
            .build();
        mGoogleApiClient.connect();
    }
    
  10. 为按钮点击添加以下方法:

    public void getLocation(View view) {
        try {
            Location lastLocation = LocationServices.FusedLocationApi.getLastLocation(
                mGoogleApiClient);
            if (lastLocation != null) {
                mTextView.setText(
                    DateFormat.getTimeInstance().format(lastLocation.getTime()) + "\n" + "Latitude="+lastLocation.getLatitude() + "\n" + "Longitude=" + lastLocation.getLongitude());
            } else {
                Toast.makeText(MainActivity.this, "null", Toast.LENGTH_LONG).show();
            }
        }
        catch (SecurityException e) {e.printStackTrace();}
    }
    
  11. 您已准备好在设备或模拟器上运行应用程序。

工作原理...

在我们调用getLastLocation()方法之前,需要设置GoogleApiClient。我们在setupGoogleApiClient()方法中调用GoogleApiClient.Builder方法,然后连接到库。当库准备就绪时,它会调用我们的ConnectionCallbacks.onConnected()方法。出于演示目的,这里是我们启用按钮的地方。(在后续的食谱中,我们将使用此回调启动附加功能。)

我们使用了按钮来显示我们可以按需调用getLastLocation();这不是一次性的调用。系统负责更新位置,并在重复调用时可能返回相同的最后位置。(这可以在时间戳中看到——它是位置时间戳,而不是按下按钮时的时间戳。)

这种按需调用位置的方法在您只需要在应用程序中发生某些事情时获取位置(例如对对象进行地理编码)的情况下可能很有用。由于系统负责位置更新,因此您的应用程序不会因为位置更新而导致电池耗尽。

我们收到的位置对象的精确度基于我们的权限设置。我们使用了ACCESS_COARSE_LOCATION,但如果我们想要更高的精确度,可以改为请求ACCESS_FINE_LOCATION,并使用以下权限:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

最后,为了使代码专注于GoogleApiClient,我们只需用SecurityException包装getLastLocation()。在生产应用程序中,您应该检查并请求上一章显示的权限。(请参阅新的运行时权限模型。)

还有更多...

如果在连接到GoogleApiClient时出现问题,将调用OnConnectionFailedListener。在这个例子中,我们显示了一个 Toast。下一个食谱,解决 GoogleApiClient OnConnectionFailedListener 报告的问题,将展示一种更健壮的方式来处理这种情况。

测试位置可能是一个挑战,因为在测试和调试时实际上移动设备是困难的。幸运的是,我们有能力用模拟器模拟 GPS 数据。(也可以在物理设备上创建模拟位置,但这并不容易。)

模拟位置

有三种方法可以在模拟器中模拟位置:

  • Android Studio

  • DDMS

  • 通过 Telnet 的Geo命令

若要在 Android Studio 中设置模拟位置,请按照以下步骤操作:

  1. 导航至工具 | Android | Android Device Monitor

  2. 在设备窗口中选择Emulator Control标签页。

  3. Location Controls下输入 GPS 坐标。

下面是一个显示Location Controls的截图:

模拟位置

提示

请注意,模拟位置是通过发送 GPS 数据来实现的。因此,为了让你的应用接收模拟位置,它需要接收 GPS 数据。测试lastLocation()可能不会发送模拟 GPS 数据,因为它并不完全依赖 GPS 来确定设备位置。尝试使用教程如何接收位置更新设置模拟位置,在那里我们可以请求优先级。(我们无法强制系统使用任何特定的位置传感器,我们只能提出请求。系统会选择最佳方案来提供结果。)

另请参阅

解决使用 GoogleApiClient OnConnectionFailedListener 报告的问题

鉴于 Google API 的不断变化,你的用户可能会尝试使用你的应用程序,但由于文件过时而无法使用。在之前的示例中,我们只是显示了一个 Toast,但我们还可以做得更好。我们可以使用GoogleApiAvailability库来显示一个对话框,帮助用户解决问题。

我们将继续之前的教程,并在onConnectionFailed()回调中添加代码。我们将使用错误结果向用户显示更多信息,以解决他们的问题。

准备工作

本教程将从之前的教程如何获取最后的位置继续。如果你是从下载的源文件中加载项目,它被称为HandleGoogleAPIError

如何操作...

由于我们是从上一个教程继续,我们只涵盖更新之前代码所需的步骤。打开ActivityMain.java并按照以下步骤操作:

  1. 在全局类变量中添加以下行:

    private final int REQUEST_RESOLVE_GOOGLE_CLIENT_ERROR=1;
    boolean mResolvingError;
    
  2. 添加以下方法以显示 Google API 错误对话框:

    private void showGoogleAPIErrorDialog(int errorCode) {
      GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
      Dialog errorDialog = googleApiAvailability.getErrorDialog(this, errorCode, REQUEST_RESOLVE_GOOGLE_CLIENT_ERROR);
      errorDialog.show();
    }
    
  3. 添加以下代码以覆盖onActivityResult()

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      if (requestCode == REQUEST_RESOLVE_GOOGLE_CLIENT_ERROR) {
        mResolvingError = false;
        if (resultCode == RESULT_OK && !mGoogleApiClient.isConnecting() && !mGoogleApiClient.isConnected()) {
            mGoogleApiClient.connect();
        }
      }
    }
    
  4. onConnectionFailed()中,使用以下代码替换调用 Toast 的现有行:

    if (mResolvingError) {
      return;
    } else if (connectionResult.hasResolution()) {
      mResolvingError = true;
      try {
        connectionResult.startResolutionForResult(MainActivity.this, REQUEST_RESOLVE_GOOGLE_CLIENT_ERROR);
      } catch (IntentSender.SendIntentException e) {
        mGoogleApiClient.connect();
      }
    } else {
      showGoogleAPIErrorDialog(connectionResult.getErrorCode());
    }
    
  5. 你已经准备好在设备或模拟器上运行应用程序。

工作原理...

与之前使用 Toast 显示错误消息不同,我们现在检查 connectionResult 以了解我们能做什么。GoogleAPIClient 使用 connectionResult 指示可能的行动方案。我们可以按如下方式调用 hasResolution() 方法:

connectionResult.hasResolution()

如果响应为 true,则用户可以解决,例如启用位置服务。如果响应为 false,我们将获取 GoogleApiAvailability 的实例并调用 getErrorDialog() 方法。完成后,我们的 onActivityResult() 回调将被调用,在那里我们重置 mResolvingError,如果成功,尝试重新连接。

提示

如果你没有带有旧版 Google API 的设备进行测试,你可以尝试在带有旧版 Google API 版本的模拟器上进行测试。

还有更多内容...

如果你的应用正在使用片段,你可以使用以下代码获取一个对话框片段:

ErrorDialogFragment errorFragment = new ErrorDialogFragment();
Bundle args = new Bundle();
args.putInt("dialog_error", errorCode);
errorFragment.setArguments(args);
errorFragment.show(getSupportFragmentManager(), "errordialog");

另请参阅

如何接收位置更新

如果你的应用需要频繁的位置更新,你的应用可以请求周期性的更新。本示例将使用 GoogleApiClientrequestLocationUpdates() 方法来演示这一点。

准备工作

在 Android Studio 中创建一个新项目,命名为 LocationUpdates。使用默认的 Phone & Tablet 选项,并在提示选择 Activity Type 时选择 Empty Activity

如何操作...

由于我们将从系统接收更新,因此这个示例不需要按钮。我们的布局将只包含 TextView 以查看位置数据。打开 Android 清单文件并按照以下步骤操作:

  1. 添加以下权限:

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    
  2. 打开 build.gradle (Module: app) 文件,并在 dependencies 部分添加以下声明:

    compile 'com.google.android.gms:play-services:8.4.0'
    
  3. 打开 activity_main.xml 文件,并用以下 XML 替换现有的 TextView

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    
  4. 打开 MainActivity.java 文件,并添加以下全局变量:

    GoogleApiClient mGoogleApiClient;
    LocationRequest mLocationRequest;
    TextView mTextView;
    
  5. 创建以下 LocationListener 类:

    LocationListener  mLocationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            if (location != null) {
                mTextView.setText(
                    DateFormat.getTimeInstance().format(location.getTime()) + "\n" + "Latitude="+location.getLatitude()+"\n" + "Longitude="+location.getLongitude());
            }
        }
    };
    
  6. 创建一个 ConnectionCallbacks 类以接收位置更新:

    GoogleApiClient.ConnectionCallbacks mConnectionCallbacks = new GoogleApiClient.ConnectionCallbacks() {
        @Override
        public void onConnected(Bundle bundle) {
            Log.i("onConnected()", "start");
            try {
                LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, mLocationListener);
            } catch (SecurityException e) {
                Log.i("onConnected()","SecurityException: "+e.getMessage());
            }
        }
        @Override
        public void onConnectionSuspended(int i) {}
    };
    
  7. 创建一个 OnConnectionFailedListener 类:

    GoogleApiClient.OnConnectionFailedListener mOnConnectionFailedListener = new GoogleApiClient.OnConnectionFailedListener() {
        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {
            Toast.makeText(MainActivity.this, connectionResult.toString(), Toast.LENGTH_LONG).show();
            Log.i("onConnected()", "SecurityException: " +connectionResult.toString());
        }
    };
    
  8. 在现有的 onCreate() 回调中添加以下代码:

    mTextView = (TextView) findViewById(R.id.textView);
    setupLocationRequest();
    
  9. 创建 setupLocationRequest() 方法:

    protected synchronized void setupLocationRequest() {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(10000);
        mLocationRequest.setFastestInterval(10000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(mConnectionCallbacks)
                .addOnConnectionFailedListener(mOnConnectionFailedListener)
                .addApi(LocationServices.API)
                .build();
        mGoogleApiClient.connect();
    }
    
  10. 你可以准备在设备或模拟器上运行应用程序了。

工作原理...

本示例与 如何获取最后位置 的示例相似,因为我们需要像之前一样设置 GoogleApiClient。但是,不是按需调用 lastLocation() 方法,我们调用 requestLocationUpdates() 方法,通过 LocationListener 类接收周期性的位置更新。

requestLocationUpdates() 方法需要三个参数:

  • GoogleApiClient

  • LocationRequest

  • LocationListener

我们像之前一样创建 GoogleApiClient。这是我们创建 LocationRequest 的代码:

mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(10000);
mLocationRequest.setFastestInterval(10000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)

调用setInterval()时,通常最好使用对您目的来说最慢的延迟,因为它需要更少的设备资源。调用setPriority()时也是同样的道理。第三个参数LocationListener是我们定义回调方法onLocationChanged()的地方。在这里,我们仅显示位置数据以及位置时间戳。

还有更多...

与之前的 Android API 不同,GoogleApiClient API 不允许为位置更新选择特定的传感器。如如何获取最后位置模拟位置部分所述,使用LocationRequest.PRIORITY_HIGH_ACCURACY以及ACCESS_FINE_LOCATION权限应该会使用 GPS 传感器。有关模拟位置的说明,请参考模拟位置部分。

停止接收位置更新

当您的应用程序不再需要位置更新时,请调用removeLocationUpdates()方法,如下所示:

LocationServices.FusedLocationApi.removeLocationUpdates(
    mGoogleApiClient, mLocationListener);

通常,当您的应用程序不再处于前台时,您可能希望关闭更新,但这取决于您的具体应用程序需求。如果您的应用程序需要持续更新,可能更希望创建一个后台服务来处理回调。

另请参阅

创建并监控一个地理围栏

如果您的应用程序需要知道用户进入某个特定位置的时间,有一种替代方法可以避免不断检查用户位置:地理围栏。地理围栏是一个带有半径的地理位置(纬度和经度)。您可以创建一个地理围栏,当用户进入您指定的位置范围时,系统会通知您。(Android 目前允许每个用户最多设置 100 个地理围栏。)

地理围栏属性包括:

  • 位置:经度和纬度

  • 半径:圆的大小(以米为单位)

  • 逗留延迟:用户在发送通知前可以在半径内停留的时间

  • 过期时间:地理围栏自动过期的时长

  • 过渡 类型:以下列出了这些类型:

    • GEOFENCE_TRANSITION_ENTER

    • GEOFENCE_TRANSITION_EXIT

    • INITIAL_TRIGGER_DWELL

本指南将向您展示如何创建一个地理围栏对象,并使用它来创建一个GeofencingRequest实例。

准备就绪

在 Android Studio 中创建一个新项目,命名为Geofence。使用默认的手机 & 平板选项,在选择活动类型时,选择空活动

如何操作...

由于我们将使用提示信息和通知与用户互动,因此本指南不需要布局。我们需要为IntentService创建一个额外的 Java 类,用于处理地理围栏警报。打开 Android Manifest 文件,按照以下步骤操作:

  1. 添加以下权限:

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    
  2. 打开文件build.gradle (Module: app)并在dependencies部分添加以下声明:

    compile 'com.google.android.gms:play-services:8.4.0'
    
  3. 创建一个名为GeofenceIntentService的新 Java 类并继承IntentService类。声明如下所示:

    public class GeofenceIntentService extends IntentService {
    
  4. 添加以下构造函数:

    public GeofenceIntentService() {
        super("GeofenceIntentService");
    }
    
  5. 添加onHandleIntent()以接收 Geofence 警告:

    protected void onHandleIntent(Intent intent) {
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
        if (geofencingEvent.hasError()) {
            Toast.makeText(getApplicationContext(), "Geofence error code= " + geofencingEvent.getErrorCode(), Toast.LENGTH_SHORT).show();
            return;
        }
        int geofenceTransition = geofencingEvent.getGeofenceTransition();
        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_DWELL) {
            sendNotification();
        }
    }
    
  6. 添加sendNotification()方法以向用户显示消息:

    private void sendNotification() {
        Log.i("GeofenceIntentService", "sendNotification()");
        Uri notificationSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("Geofence Alert")
                .setContentText("GEOFENCE_TRANSITION_DWELL")
                .setSound(notificationSoundUri)
                .setLights(Color.BLUE, 500, 500);
        NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(0, notificationBuilder.build());
    }
    
  7. 打开 Android 清单并在与<activity>元素同一级别的<application>元素内添加以下内容:

    <service android:name=".GeofenceIntentService"/>
    
  8. 打开MainActivity.java并添加以下全局变量:

    private final int MINIMUM_RECOMENDED_RADIUS=100;
    GoogleApiClient mGoogleApiClient;
    PendingIntent mGeofencePendingIntent;
    
  9. 创建以下ResultCallback类:

    ResultCallback mResultCallback = new ResultCallback() {
        @Override
        public void onResult(Result result) {
            Log.i("onResult()", "result: " + result.getStatus().toString());
        }
    };
    
  10. 创建一个ConnectionCallbacks类:

    GoogleApiClient.ConnectionCallbacks mConnectionCallbacks = new GoogleApiClient.ConnectionCallbacks() {
        @Override
        public void onConnected(Bundle bundle) {
            try {
              LocationServices.GeofencingApi.addGeofences(
                  mGoogleApiClient,
                  createGeofencingRequest(),
                  getGeofencePendingIntent()
              ).setResultCallback(mResultCallback);
            } catch (SecurityException e) {
                Log.i("onConnected()", "SecurityException: " + e.getMessage());
            }
        }
        @Override
        public void onConnectionSuspended(int i) {}
    };
    
  11. 创建一个OnConnectionFailedListener类:

    GoogleApiClient.OnConnectionFailedListener mOnConnectionFailedListener = new GoogleApiClient.OnConnectionFailedListener() {
        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {
            Log.i("onConnectionFailed()", "connectionResult: " +connectionResult.toString());
        }
    };
    
  12. 在现有的onCreate()回调中添加以下代码:

      setupGoogleApiClient();
    
  13. 添加设置GoogleAPIClient的方法:

    protected synchronized void setupGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addConnectionCallbacks(mConnectionCallbacks)
            .addOnConnectionFailedListener(mOnConnectionFailedListener)
            .addApi(LocationServices.API)
            .build();
        mGoogleApiClient.connect();
    }
    
  14. 创建setupGoogleApiClient()方法:

    protected synchronized void setupGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addConnectionCallbacks(mConnectionCallbacks)
            .addOnConnectionFailedListener(mOnConnectionFailedListener)
            .addApi(LocationServices.API)
            .build();
        mGoogleApiClient.connect();
    }
    
  15. 使用以下方法创建一个待定意图:

    private PendingIntent getGeofencePendingIntent() {
        if (mGeofencePendingIntent != null) {
            return mGeofencePendingIntent;
        }
        Intent intent = new Intent(this, GeofenceIntentService.class);
        return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    }
    
  16. 创建一个geofence对象并将其添加到请求的列表中:

    private List createGeofenceList() {
        List<Geofence> geofenceList = new ArrayList<Geofence>();
        geofenceList.add(new Geofence.Builder()
                .setRequestId("GeofenceLocation")
                .setCircularRegion(
                        37.422006, //Latitude
                        -122.084095, //Longitude
                        MINIMUM_RECOMENDED_RADIUS)
                .setLoiteringDelay(30000)
                .setExpirationDuration(Geofence.NEVER_EXPIRE)
                .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_DWELL)
        .build());
        return geofenceList;
    }
    
  17. 按如下方式创建createGeofencingRequest()方法:

    private GeofencingRequest createGeofencingRequest() {
        GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
        builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_DWELL);
        builder.addGeofences(createGeofenceList());
        return builder.build();
    }
    
  18. 你现在可以在设备或模拟器上运行应用程序了。

工作原理...

首先,我们添加ACCESS_FINE_LOCATION权限,因为这是 Geofencing 所必需的。我们像在以前的食谱中一样设置GoogleApiClient,并等待onConnected()被调用以设置GeofencingApi

在我们可以调用GeofencingApi.addGeofences()方法之前,我们必须准备三个对象:

  • GoogleApiClient

  • Geofence 请求

  • 待定意图

我们已经创建了GoogleApiClient,我们将其保存在mGoogleApiClient中。

要创建 Geofence 请求,我们使用GeofencingRequest.Builder。构建器需要 Geofence 对象列表,这些对象在createGeofenceList()方法中创建。(即使我们只创建了一个 Geofence 对象,构建器也需要一个列表,所以我们只需将单个 Geofence 添加到ArrayList中。)以下是设置 Geofence 属性的地方:

.setRequestId("GeofenceLocation")
.setCircularRegion(
        37.422006, //Latitude
        -122.084095, //Longitude
        MINIMUM_RECOMENDED_RADIUS)
.setLoiteringDelay(30000)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_DWELL)

只有 Loitering 延迟是可选的,但我们需要它,因为我们使用了DWELL转换。在调用setTransitionTypes()时,我们可以使用管道显示的OR运算符组合多个转换类型。以下是使用ENTEREXIT的示例:

.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT)

在这个例子中,我们使用了与模拟器相同的默认纬度和经度。根据需要更改这些值。

我们调用Geofence.Builder()创建 Geofence 对象。准备好 Geofence 列表后,我们调用GeofencingRequest.Builder并将我们的初始触发器设置为INITIAL_TRIGGER_DWELL。(如果你更改前面的转换类型,你可能还需要更改初始触发器。)

我们需要的最后一个对象是 Pending Intent,这是系统在满足 Geofence 条件时通知我们应用的途径。我们创建了 GeofenceIntentService 来处理 Geofence 意图,通过向用户发送通知。(有关通知的更多信息,请参阅第七章中的 使用通知的灯光、动作和声音 Redux 配方,警报和通知。)

创建了所有三个对象后,我们只需调用 LocationServices.GeofencingApi.addGeofences() 并等待通知的到来。

还有更多...

若要停止接收 Geofence 通知,可以调用 removeGeofences() 方法,并使用 RequestID 参数或 PendingIntent。以下示例使用了与通知相同的 PendingIntent 方法:

LocationServices.GeofencingApi.removeGeofences(
    mGoogleApiClient,
    getGeofencePendingIntent()
).setResultCallback(mResultCallback);

另请参阅

第十四章:为 Play 商店准备你的应用

在本章中,我们将涵盖以下主题:

  • 新的 Android 6.0 运行时权限模型

  • 如何安排闹钟

  • 接收设备启动通知

  • 使用 AsyncTask 进行后台工作

  • 将语音识别添加到你的应用

  • 使用 Google 云消息推送通知

  • 如何将谷歌登录添加到你的应用

引言

当我们接近这本书的结尾时,是时候在发布到 Play 商店之前为你的应用添加最后的润色了。本章的食谱涵盖了可能决定用户保留还是卸载你的应用的主题。

我们的第一部分,新的 Android 6.0 运行时权限模型,无疑是一个重要的话题,可能是 Android 从版本 5.x 升级到版本 6 的主要原因!对 Android 权限模型的更改已经被请求了一段时间,所以这个新模型是一个受欢迎的改变,至少对用户来说是这样。

接下来,我们将看看在 Android 中使用闹钟。闹钟的主要优点之一是操作系统负责维护闹钟,即使你的应用没有运行。由于闹钟在设备重启后不会保留,我们还将看看如何在接收设备启动通知中检测设备重启,以便你可以重新创建你的闹钟。

几乎任何严肃的 Android 应用都需要一种方法在主线程之外执行可能阻塞的任务。否则,你的应用可能会被视为反应迟钝,或者更糟,完全无响应。AsyncTask旨在使创建后台工作线程更容易,我们将在使用 AsyncTask 进行后台工作这一部分中演示。

如果你希望你的应用能够从免提输入或语音识别中受益,请查看一下将语音识别添加到你的应用这一部分,我们将探讨谷歌语音 API。

与你的用户沟通的最有趣的功能之一可能是推送通知或谷歌所谓的Google Cloud Messaging (GCM)。使用 Google 云消息推送通知这一部分将指导你将 GCM 添加到你的应用程序,并解释更大的图景。

最后,我们将在如何将谷歌登录添加到你的应用这一部分结束本章,展示如何让你的应用更加舒适并鼓励用户登录。

新的 Android 6.0 运行时权限模型

旧的安全模型是 Android 中许多人的痛点。评论中经常看到对应用所需权限的评论是很常见的。有时,权限是过分的(比如一个手电筒应用需要网络权限),但其他时候,开发者请求某些权限是有充分理由的。主要问题是这是一个全有或全无的前景。

这最终在 Android 6 Marshmallow(API 23)版本中得到了改变。新的权限模型仍然像以前一样在清单中声明权限,但用户可以选择性地接受或拒绝每个权限。用户甚至可以撤销之前授予的权限。

尽管这对于许多人是受欢迎的改变;然而,对于开发者来说,这可能会破坏之前正常工作的代码。我们之前讨论过这个权限变化,因为它具有深远的影响。这个菜谱将汇总所有信息,以便在您自己的应用中实施此更改时作为单一参考点。

需要记住的一个重要点是,这个变化只影响 Android 6.0(API 23)及以上版本的用户。

准备就绪

在 Android Studio 中创建一个新项目,并将其命名为 RuntimePermission。对于 Activity 类型,使用默认的 Phone & Tablet 选项并选择 Empty Activity

示例源代码将最低 API 设置为 23,但这并不是必须的。如果您的 compileSdkVersion 是 API 23 或以上,编译器将针对新的安全模型标记您的代码。

如何操作...

我们需要先在清单中添加我们所需的权限,然后我们将添加一个按钮来调用我们的检查权限代码。打开 Android 清单并按照以下步骤操作:

  1. 添加以下权限:

    <uses-permission android:name="android.permission.SEND_SMS"/>
    
  2. 打开 activity_main.xml 并用此按钮替换现有的 TextView

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Do Something"
        android:layout_centerInParent="true"
        android:onClick="doSomething"/>
    
  3. 打开 MainActivity.java 并向类中添加以下常量:

    private final int REQUEST_PERMISSION_SEND_SMS=1;
    
  4. 添加此方法来检查权限:

    private boolean checkPermission(String permission) {
        int permissionCheck = ContextCompat.checkSelfPermission(
                this, permission);
        return (permissionCheck == PackageManager.PERMISSION_GRANTED);
    }
    
  5. 添加此方法以显示解释对话框:

    private void showExplanation(String title,
        String message, final String permission, final int permissionRequestCode) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(title).setMessage(message).setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                requestPermission(permission, permissionRequestCode);
            }
        });
        builder.create().show();
    }
    
  6. 添加此方法来请求权限:

    private void requestPermission(String permissionName, int permissionRequestCode) {
        ActivityCompat.requestPermissions(this, new String[]{permissionName}, permissionRequestCode);
    }
    
  7. 添加按钮点击的方法:

    public void doSomething(View view) {
        if (!checkPermission(Manifest.permission.SEND_SMS)) {
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.SEND_SMS)) {
                showExplanation("Permission Needed", "Rationale", Manifest.permission.SEND_SMS, REQUEST_PERMISSION_SEND_SMS);
            } else {
                requestPermission(Manifest.permission.SEND_SMS, REQUEST_PERMISSION_SEND_SMS);
            }
        } else {
            Toast.makeText(MainActivity.this, "Permission (already) Granted!", Toast.LENGTH_SHORT).show();
        }
    }
    
  8. 如下重写 onRequestPermissionsResult()

    @Override
    public void onRequestPermissionsResult(
        int requestCode,
        String permissions[],
        int[] grantResults) {
        switch (requestCode) {
            case REQUEST_PERMISSION_SEND_SMS: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Toast.makeText(MainActivity.this, "Permission Granted!", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(MainActivity.this, "Permission Denied!", Toast.LENGTH_SHORT).show();
                }
                return;
            }
        }
    }
    
  9. 现在,你可以在设备或模拟器上运行应用程序了。

它是如何工作的...

使用新的运行时权限模型涉及以下内容:

  1. 检查你是否拥有所需的权限。

  2. 如果没有,检查我们是否应该显示理由(意味着,之前的请求被拒绝了)。

  3. 请求权限;只有操作系统可以显示权限请求。

  4. 处理请求响应。

以下是相应的方法:

  • ContextCompat.checkSelfPermission

  • ActivityCompat.requestPermissions

  • ActivityCompat.shouldShowRequestPermissionRationale

  • onRequestPermissionsResult

    注意

    尽管你在运行时请求权限,但所需的权限必须在 Android 清单中列出。如果未指定权限,操作系统将自动拒绝请求。

还有更多...

你可以通过 ADB 使用以下命令来授权/撤销权限:

adb shell pm [grant|revoke] <package> <permission-name>

这是一个示例,为我们的测试应用授予 SEND_SMS 权限:

adb shell pm grant com.packtpub.androidcookbook.runtimepermissions android.permission.SEND_SMS

另请参阅

如何计划一个闹钟

Android 提供了 AlarmManager 来创建和计划闹钟。闹钟提供以下功能:

  • 计划在设定的时间或间隔触发报警

  • 由操作系统维护,而不是您的应用程序,因此即使您的应用程序没有运行,或者设备在休眠,也会触发报警

  • 可用于触发周期性任务(例如每小时新闻更新),即使应用程序没有运行

  • 您的应用程序不使用资源(如计时器或后台服务),因为操作系统管理调度

如果您在应用程序运行时只需要一个简单的延迟,例如,一个 UI 事件的短暂延迟,报警并不是最佳解决方案。对于短暂延迟,使用处理程序更容易、更高效,正如我们在之前的几个食谱中所做的那样。

使用报警时,请记住以下最佳实践:

  • 尽可能使用不频繁的报警时间

  • 避免唤醒设备

  • 尽可能使用不精确的时间——时间越精确,所需的资源越多

  • 避免基于时钟时间设置报警(例如 12:00);如果可能,添加随机调整以避免服务器拥堵(尤其是在检查新内容,如天气或新闻时尤为重要)

报警有三个属性,如下所示:

  • 报警类型(见以下列表)

  • 触发时间(如果时间已经过去,则立即触发报警)

  • 待定意图

重复报警具有相同的三个属性,以及一个间隔:

  • 报警类型(见以下列表)

  • 触发时间(如果时间已经过去,则立即触发)

  • 间隔

  • 待定意图

有四种报警类型:

  • RTC实时时钟):这是基于墙钟时间。它不会唤醒设备。

  • RTC_WAKEUP:这是基于墙钟时间。如果设备在休眠,它会唤醒设备。

  • ELAPSED_REALTIME:这是基于设备自启动以来的时间。它不会唤醒设备。

  • ELAPSED_REALTIME_WAKEUP:这是基于设备自启动以来的时间。如果设备在休眠,它会唤醒设备。

Elapsed Real Time 更适合时间间隔报警——例如每 30 分钟。

提示

设备重启后,报警不会保留。设备关闭时,所有报警都会被取消,因此,在设备启动时重置报警是您应用程序的责任。(更多信息请参见接收设备启动通知。)

以下食谱将演示如何使用 AlarmManager 创建报警。

准备就绪

在 Android Studio 中创建一个新项目,并将其命名为:Alarms。选择默认的手机和平板选项,并在提示活动类型时选择空活动

如何操作...

设置报警需要一个待定意图,当触发报警时,Android 会发送该意图。因此,我们需要设置一个广播接收器来捕获报警意图。我们的用户界面将仅包含一个简单按钮来设置报警。首先,打开 Android 清单文件并按照以下步骤操作:

  1. 在与现有 <activity> 元素同一级别的 <application> 元素中添加以下 <receiver>

    <receiver android:name=".AlarmBroadcastReceiver">
        <intent-filter>
            <action android:name="com.packtpub.androidcookbook.alarms.ACTION_ALARM" />
        </intent-filter>
    </receiver>
    
  2. 打开 activity_main.xml 文件,将现有的 TextView 替换为以下按钮:

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Set Alarm"
        android:layout_centerInParent="true"
        android:onClick="setAlarm"/>
    
  3. 使用以下代码创建一个名为 AlarmBroadcastReceiver 的新 Java 类:

    public class AlarmBroadcastReceiver extends BroadcastReceiver {
    
        public static final String ACTION_ALARM="com.packtpub.androidcookbook.alarms.ACTION_ALARM";
    
        @Override
        public void onReceive(Context context, Intent intent) {
            if (ACTION_ALARM.equals(intent.getAction())) {
                Toast.makeText(context, ACTION_ALARM, Toast.LENGTH_SHORT).show();
            }
        }
    }
    
  4. 打开 ActivityMain.java 并添加按钮点击的方法:

    public void setAlarm(View view) {
        Intent intentToFire = new Intent(getApplicationContext(), AlarmBroadcastReceiver.class);
        intentToFire.setAction(AlarmBroadcastReceiver.ACTION_ALARM);
        PendingIntent alarmIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intentToFire, 0);
        AlarmManager alarmManager = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
        long thirtyMinutes=SystemClock.elapsedRealtime() + 30 * 60 * 1000;
        alarmManager.set(AlarmManager.ELAPSED_REALTIME, thirtyMinutes, alarmIntent);
    }
    
  5. 你已经准备好在设备或模拟器上运行应用程序。

工作原理...

创建闹钟是通过以下这行代码完成的:

alarmManager.set(AlarmManager.ELAPSED_REALTIME, thirtyMinutes, alarmIntent);

这是方法的签名:

set(AlarmType, Time, PendingIntent);

注意

在 Android 4.4 KitKat(API 19)之前,这是请求确切时间的方法。从 Android 4.4 开始,出于效率考虑,这被视为一个非确切时间,但不会在请求的时间之前传递意图。(如果你需要确切时间,请参考下面的 setExact() 方法。)

为了设置闹钟,我们创建了一个带有之前定义的闹钟动作的待定意图:

public static final String ACTION_ALARM="com.packtpub.androidcookbook.alarms.ACTION_ALARM";

(这是一个任意字符串,可以是任何我们想要的内容,但它需要是唯一的,因此我们在前面加上我们的包名。)我们在广播接收器的 onReceive() 回调中检查这个动作。

还有更多...

如果你点击 设置闹钟 按钮,等待三十分钟,当闹钟触发时你会看到 Toast。如果你在第一个闹钟触发之前就迫不及待地再次点击 设置闹钟 按钮,你不会得到两个闹钟。相反,操作系统将用新的闹钟替换第一个闹钟,因为它们都使用相同的待定意图。(如果你需要多个闹钟,你需要创建不同的待定意图,比如使用不同的动作。)

取消闹钟

如果你想要取消闹钟,通过传递用于创建闹钟的相同待定意图来调用 cancel() 方法。如果我们继续按照我们的指南操作,这将是这样子的:

alarmManager.cancel(alarmIntent);

重复闹钟

如果你想创建一个重复的闹钟,请使用 setRepeating() 方法。它的签名与 set() 方法类似,但包含一个间隔。如下所示:

setRepeating(AlarmType, Time (in milliseconds), Interval, PendingIntent);

对于间隔,你可以以毫秒为单位指定间隔时间,或者使用 AlarmManager 的预定义常量之一:

  • INTERVAL_DAY

  • INTERVAL_FIFTEEN_MINUTES

  • INTERVAL_HALF_DAY

  • INTERVAL_HALF_HOUR

  • INTERVAL_HOUR

另请参阅

接收设备启动通知

安卓在其生命周期中发送许多意图。最早发送的意图之一是 ACTION_BOOT_COMPLETED。如果你的应用程序需要知道设备何时启动,你需要捕获这个意图。

本指南将引导你完成在设备启动时接收通知所需的步骤。

准备就绪

在 Android Studio 中创建一个新项目,并将其命名为 DeviceBoot。在选择 Activity Type 时,使用默认的Phone & Tablet选项并选择Empty Activity

如何操作...

首先,打开 Android Manifest 文件并按照以下步骤操作:

  1. 添加以下权限:

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    
  2. <application> 元素中添加以下 <receiver>,与现有的 <activity> 元素同一级别:

    <receiver android:name=".BootBroadcastReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
        </intent-filter>
    </receiver>
    
  3. 使用以下代码创建一个名为 BootBroadcastReceiver 的新 Java 类:

    public class BootBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
                Toast.makeText(context, "BOOT_COMPLETED", Toast.LENGTH_SHORT).show();
            }
        }
    }
    
  4. 重启设备以查看提示消息。

工作原理...

当设备启动时,Android 会发送BOOT_COMPLETED意图。只要我们的应用程序有接收意图的权限,我们就会在广播接收器中收到通知。

要实现这一功能,需要考虑以下三个方面:

  • RECEIVE_BOOT_COMPLETED的权限

  • BOOT_COMPLETED添加到接收意图过滤器中

  • 在广播接收器中检查BOOT_COMPLETED动作

显然,你希望用你自己的代码替换提示消息,比如重新创建你可能需要的任何闹钟。

还有更多...

如果你按照前面的步骤操作,那么你已经有一个广播接收器了。不需要为每个动作分别创建BroadcastReceiver,只需根据需要检查每个动作即可。以下是如果我们需要处理另一个动作的示例:

public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
        Toast.makeText(context, "BOOT_COMPLETED", Toast.LENGTH_SHORT).show();
    } else if (intent.getAction().equals("<another_action>")) {
        //handle another action
    }
}

另请参阅

使用 AsyncTask 进行后台工作

在整本书中,我们提到了不要阻塞主线程的重要性。在主线程上执行长时间运行的操作可能会导致你的应用程序看起来反应迟钝,甚至挂起。如果你的应用程序在约 5 秒内没有响应,系统可能会显示应用程序无响应ANR)对话框,并给出终止你应用程序的选项。(这是你要避免的事情,因为这会导致你的应用程序被卸载。)

Android 应用程序使用单线程模型,有两个简单的规则,如下:

  • 不要阻塞主线程

  • 所有 UI 操作应该在主线程上执行

当 Android 启动你的应用程序时,它会自动创建主线程(或 UI 线程)。所有 UI 操作都必须从这条线程中调用。第一条规则是“不要阻塞主线程”。这意味着你需要为任何长时间运行或可能阻塞的任务创建一个后台线程或工作线程。这就是为什么所有基于网络的任务都应该在主线程之外执行。

Android 在处理后台线程时提供以下选项:

  • Activity.runOnUiThread()

  • View.post()

  • View.postDelayed()

  • Handler

  • AsyncTask

本教程将探讨AsyncTask类;由于它之前已经创建过,你无需直接使用 Handler 或 post 方法。

准备工作

在 Android Studio 中创建一个新项目,并将其命名为:AsyncTask。选择默认的手机 & 平板选项,并在提示活动类型时选择空活动

如何操作...

这个示例我们只需要一个按钮。打开activity_main.xml并按照以下步骤操作:

  1. 使用以下按钮替换现有的 TextView:

    <Button
        android:id="@+id/buttonStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start"
        android:layout_centerInParent="true"
        android:onClick="start" />
    
  2. 打开MainActivity.java并添加以下全局变量:

    Button mButtonStart;
    
  3. 添加AsyncTask类:

    private class CountingTask extends AsyncTask<Integer, Integer, Integer> {
        @Override
        protected Integer doInBackground(Integer... params) {
            int count = params[0];
            for (int x=0;x<=count; x++){
                //Nothing to do
            }
            return count;
        }
        @Override
        protected void onPostExecute(Integer integer) {
            super.onPostExecute(integer);
            mButtonStart.setEnabled(true);
        }
    }
    
  4. onCreate()中添加以下代码以初始化按钮:

    mButtonStart=(Button)findViewById(R.id.buttonStart);
    
  5. 添加按钮点击的方法:

    public void start(View view){
        mButtonStart.setEnabled(false);
        new CountingTask().execute(10000000);
    }
    
  6. 你已经准备好在设备或模拟器上运行应用程序了。

它是如何工作的...

这是一个非常简单的AsyncTask示例,只是为了让它工作。从技术上讲,只有doInBackground()是必需的,但通常,你可能希望在它完成时通过onPostExecute()接收通知。

AsyncTask通过为doInBackground()方法创建一个工作线程来工作,然后在 UI 线程的onPostExecute()回调中响应。

注意我们是如何等到onPostExecute()被调用之后才进行任何 UI 操作,比如启用按钮。如果我们尝试在工作线程中修改 UI,它要么无法编译,要么会抛出运行时异常。你还应该注意,我们是如何在每个按钮点击时实例化一个新的CountingTask对象。这是因为AsyncTask只能执行一次。尝试再次调用 execute 将会抛出异常。

还有更多...

AsyncTask可以非常简单,但如果你需要,它仍然具有很多灵活性选项。当与 Activity 一起使用AsyncTask时,了解 Activity 是否被销毁和重新创建(如在屏幕方向改变时)或AsyncTask是否继续运行非常重要。这可能导致你的AsyncTask成为孤儿,并且可能对已销毁的活动做出响应(导致NullPointer异常)。因此,通常使用与 Fragment 一起的AysncTask(在屏幕旋转时不会销毁),或者使用 Loader 代替。(有关 Loader 的链接请参见下一节。)

参数类型

对于许多人来说,创建自己的类时AsyncTask最令人困惑的方面是参数。如果你看我们的类声明,AsyncTask有三个参数;它们定义如下:

AsyncTask<Params, Progress, Result >

这些参数是泛型类型,使用方法如下:

  • 参数: 这是调用doInBackground()的参数类型。

  • 进度: 这是发布更新的参数类型。

  • 结果: 这是发布结果的参数类型。

当你声明自己的类时,请将参数替换为你需要的变量类型。

这是AsyncTask的流程以及上述参数的使用方法:

  • onPreExecute(): 这在doInBackground()开始之前被调用。

  • doInBackground(Params): 这在后台线程中执行。

  • onProgressUpdate(Progress): 这在 UI 线程中响应工作线程中的publishProgress(Progress)调用。

  • onPostExecute(Result): 当工作线程完成时,在 UI 线程中调用。

取消任务

要取消任务,请按照以下方式在对象上调用 cancel 方法:

< AsyncTask>.cancel(true);

你需要拥有对象实例来访问cancel()方法。(在我们的上一个示例中,我们没有保存该对象。)在设置cancel(true)之后,在doInBackground()中调用isCancelled()将返回true,这样你就可以退出循环了。如果取消,将调用onCancelled()而不是onPostExecute()

另请参阅

在你的应用中添加语音识别

Android 2.2(API 8)在 Android 中引入了语音识别功能,并且几乎在每一个新的主要 Android 版本发布时都会进行改进。本教程将演示如何使用谷歌语音服务在你的应用中添加语音识别功能。

准备工作

在 Android Studio 中创建一个新项目,将其命名为SpeechRecognition。使用默认的手机 & 平板选项,在选择活动类型时选择空活动

如何操作...

我们首先在布局中添加一个“立即说话”(或麦克风)按钮,然后添加必要的代码来调用语音识别器。打开activity_main.xml并按照以下步骤操作:

  1. 使用以下 XML 替换现有的TextView

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />
    <ImageButton
        android:id="@+id/imageButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:src="img/ic_btn_speak_now"
        android:onClick="speakNow"/>
    
  2. 定义REQUEST_SPEECH常量:

    private final int REQUEST_SPEECH=1;
    
  3. 在现有的onCreate()回调中添加以下代码:

    PackageManager pm = getPackageManager();
    List<ResolveInfo> activities = pm.queryIntentActivities(
        new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
    if (activities.size() == 0) {
        findViewById(R.id.imageButton).setEnabled(false);
        Toast.makeText(this, "Speech Recognition Not Supported", Toast.LENGTH_LONG).show();
    }
    
  4. 添加按钮点击方法:

    public void speakNow(View view) {
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
            RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        startActivityForResult(intent, REQUEST_SPEECH);
    }
    
  5. 添加以下代码以覆盖onActivityResult()回调:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode==REQUEST_SPEECH && resultCode == RESULT_OK && data!=null) {
            ArrayList<String> result = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
            TextView textView = (TextView)findViewById(R.id.textView);
            if (result.size()>0){
                textView.setText("");
                for (String item : result ) {
                    textView.append(item+"\n");
                }
            }
        }
    }
    
  6. 你已经准备好在设备或模拟器上运行应用程序。

工作原理...

这里的工作由 Android 中包含的谷歌语音识别器完成。为了确保设备上可用该服务,我们在onCreate()中调用PackageManager。如果至少有一个活动注册以处理RecognizerIntent.ACTION_RECOGNIZE_SPEECH意图,那么我们知道它是可用的。如果没有活动可用,我们会显示一个提示语音识别不可用并禁用麦克风按钮的 Toast。

按钮点击通过调用使用RecognizerIntent.ACTION_RECOGNIZE_SPEECH创建的意图来启动识别过程。

EXTRA_LANGUAGE_MODEL参数是必需的,有以下两个选择:

  • LANGUAGE_MODEL_FREE_FORM

  • LANGUAGE_MODEL_WEB_SEARCH

我们在onActivityResult()回调中获取结果。如果我们得到RESULT_OK,那么我们应该有一个已识别单词的列表,可以使用getStringArrayListExtra()检索该列表。该数组列表将按识别信心最高开始排序。

如果你想要获取信心评分,可以使用EXTRA_CONFIDENCE_SCORES检索浮点数组。下面是一个例子:

float[] confidence = data.getFloatArrayExtra(RecognizerIntent.EXTRA_CONFIDENCE_SCORES);

信心评分是可选的,可能不会出现。分数为 1.0 表示最高信心,而 0.0 表示最低信心。

还有更多...

使用意图是获取语音识别的快速简便方法;然而,如果你不想使用默认的谷歌活动,可以直接调用SpeechRecognizer类。以下是实例化该类的一个例子:

SpeechRecognizer speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this);

你需要添加RECORD_AUDIO权限并实现RecognitionListener类来处理语音事件。(更多信息请参见以下链接。)

另请参阅

使用 GCM 的推送通知

GCM,谷歌版的推送通知,可以让你的应用程序接收消息。这个想法与短信类似,但更加灵活。GCM 有三个组成部分:

  • 你的服务器(这是你发起消息的地方)

  • 谷歌的 GCM 服务器

  • 安卓设备(尽管 GCM 也适用于其他平台)

当用户启动你的应用程序时,你的代码需要连接到 GCM 服务器并获取一个设备令牌,然后将该令牌发送到你的服务器。你的服务器负责发起消息并将其传递给 GCM 服务器。你的服务器需要跟踪在发起消息时需要发送的设备令牌。(你的服务器告诉 GCM 服务器需要发送哪些设备令牌。)

你可以实施自己的服务器,或者选择使用许多可用的服务之一。下一章,后端服务选项,将查看几个 BaaS 选项,其中许多也提供推送通知。(简单测试选项部分提供了一个选项来验证你的代码是否正常工作。)

本指南将带你通过使用当前(版本 8.3)的谷歌服务库添加 GCM 的步骤。在开始步骤之前,值得注意的是 GCM 支持回溯到 API 8,只要用户有谷歌账户即可。在安卓 4.0.4 之后,不再需要谷歌账户。

准备工作

在 Android Studio 中创建一个新项目,并将其命名为GCM。使用默认的手机和平板选项,并在提示活动类型时选择空活动

GCM 使用谷歌服务插件,该插件需要从谷歌开发者控制台获取谷歌服务配置文件。要创建配置文件,你需要以下信息:

注意

如果你下载了源文件,按照前面的步骤操作时,你需要创建一个新的包名,因为现有的包名已经被注册了。

如何操作...

完成前面的准备工作后,按照以下步骤操作:

  1. 将你在准备工作部分下载的google-services.json文件复制到你的应用文件夹(<项目文件夹>`GCM\app`)。

  2. 打开项目 Gradle 构建文件:build.gradle (Project: GCM),并向buildscript dependencies部分添加以下内容:

    classpath 'com.google.gms:google-services:1.5.0-beta2'
    
  3. 打开 app 模块的 Gradle 构建文件:build.gradle (Module: app),并在文件开头(android部分之上)添加以下声明:

    apply plugin: 'com.google.gms.google-services'
    
  4. 在第 3 步的同一模块构建文件中,向依赖项部分添加以下声明:

    compile 'com.google.android.gms:play-services-auth:8.3.0'
    
  5. 打开 Android Manifest 文件,并添加以下权限:

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <permission android:name="< packageName >.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />
    <uses-permission android:name="< packageName >.permission.C2D_MESSAGE" />
    
  6. <application>元素内,添加以下<receiver><service>声明(这些应该与<activity>在同一级别):

    <receiver
        android:name="com.google.android.gms.gcm.GcmReceiver"
        android:exported="true"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <category android:name="<packageName>" />
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        </intent-filter>
    </receiver>
    <service
        android:name=".GCMService"
        android:exported="false" >
        <intent-filter>
            <action android:name="com.google.android.c2dm.intent.GCM_RECEIVED_ACTION"/>
            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        </intent-filter>
    </service>
    <service
        android:name=".GCMInstanceService"
        android:exported="false">
        <intent-filter>
            <action android:name="com.google.android.gms.iid.InstanceID" />
        </intent-filter>
    </service>
    <service
        android:name=".GCMRegistrationService"
        android:exported="false">
    </service>
    
  7. 创建一个名为GCMRegistrationService的新 Java 类,它扩展了IntentService,如下所示:

    public class GCMRegistrationService extends IntentService {
    
        private final String SENT_TOKEN="SENT_TOKEN";
    
        public GCMRegistrationService() {
            super("GCMRegistrationService");
        }
    
        @Override
        protected void onHandleIntent(Intent intent) {
            super.onCreate();
            SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
            try {
                InstanceID instanceID = InstanceID.getInstance(this);
                String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
                Log.i("GCMRegistrationService", "GCM Registration Token: " + token);
                //sendTokenToServer(token);
                sharedPreferences.edit().putBoolean(SENT_TOKEN, true).apply();
            } catch (Exception e) {
                sharedPreferences.edit().putBoolean(SENT_TOKEN, false).apply();
            }
        }
    }
    
  8. 创建一个名为GCMInstanceService的新 Java 类,它扩展了InstanceIDListenerService,如下所示:

    public class GCMInstanceService extends InstanceIDListenerService {
        @Override
             public void onTokenRefresh() {
            Intent intent = new Intent(this, GCMRegistrationService.class);
            startService(intent);
        }
    }
    
  9. 创建一个名为GCMService的新 Java 类,它扩展了GcmListenerService,如下所示:

    public class GCMService extends GcmListenerService {
    
        @Override
        public void onMessageReceived(String from, Bundle data) {
            super.onMessageReceived(from, data);
            Log.i("GCMService", "onMessageReceived(): " + data.toString());
        }
    }
    
  10. 在现有的onCreate()回调中添加以下代码:

    Intent intent = new Intent(this, GCMRegistrationService.class);
    startService(intent);
    
  11. 你现在可以在设备或模拟器上运行应用程序了。

工作原理...

大部分实际的 GCM 代码被封装在 Google API 中,简化了实现。我们只需要设置项目以包含 Google 服务,并给我们的应用程序所需权限。

注意

重要!在第 5 步和第 6 步添加权限时,请将<packageName>占位符替换为你的应用程序包名。

GCM 最复杂的方面可能是需要多个服务。尽管每个服务中的代码都很少,但每个服务都有特定的任务。GCM 主要有两个方面的内容:

  • 将应用与 GCM 服务器注册

  • 接收消息

这是向 GCM 服务器注册的代码:

String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);

我们不在 Activity 中调用getToken(),因为它可能会阻塞 UI 线程。相反,我们调用GCMRegistrationService,它在后台线程中处理调用。在收到设备令牌后,你需要将其发送到你的服务器,因为初始化消息时需要它。

接收 GCM 消息的过程在GCMService中处理,它扩展了GcmListenerService。由于 Google API 已经处理了大部分工作,我们只需要响应onMessageReceived()回调。

还有更多...

为了便于输入,我们省略了一个重要的谷歌服务 API 验证,这个验证在生产应用中应当被包含。我们在前面的部分中的onCreate()直接调用了GCMRegistrationService,而不是首先检查 Google API 服务是否可用。以下是一个如何调用isGooglePlayServicesAvailable()方法的示例:

private boolean isGooglePlayServicesAvailable() {
    GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
    int resultCode = googleApiAvailability.isGooglePlayServicesAvailable(this);
    if (resultCode != ConnectionResult.SUCCESS) {
        if (googleApiAvailability.isUserResolvableError(resultCode)) {
            googleApiAvailability.getErrorDialog(this, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST).show();
        } else {
            Toast.makeText(MainActivity.this, "Unsupported Device", Toast.LENGTH_SHORT).show();
            finish();
        }
        return false;
    }
    return true;
}

然后,更改onCreate()代码,首先调用这个方法:

if (isGooglePlayServicesAvailable()) {
    Intent intent = new Intent(this, GCMRegistrationService.class);
    startService(intent);
}

简单的测试选项

为了帮助验证你的代码是否正确运行,创建了一个测试应用并发布在 Google Play 上。这个应用可以在实体设备和模拟器上运行。Google Play 列表还包含一个下载源代码并直接运行项目的链接,以便更容易输入所需字段。

提示

GCM (推送通知) 测试器:更多信息请参考以下链接:

play.google.com/store/apps/details?id=com.eboyer.gcmtester

参阅

如何在应用中添加 Google 登录

Google 登录允许你的用户使用他们的 Google 凭据登录你的应用。本教程将指导你如何在应用中添加 Google 登录。以下是将在教程中创建的应用中显示的 Google 登录按钮的截图:

如何在应用中添加 Google 登录

准备工作

在 Android Studio 中创建一个新项目,命名为 GoogleSignIn。选择默认的手机 & 平板选项,并在提示活动类型时选择空活动

Google 登录使用 Google 服务插件,该插件需要一个 Google 服务配置文件,你可以在 Google 开发者控制台获取。要创建配置文件,你需要以下信息:

  • 您的应用程序包名

  • 你的签名证书的 SHA-1 哈希码(有关更多信息,请参阅教程末尾的验证客户端链接)

当你拥有这些信息后,登录此 Google 链接,并按照向导启用登录:

developers.google.com/mobile/add

注意

如果你正在下载源文件,按照前面的步骤操作时,你需要创建一个新的包名,因为现有的包名已经被注册。

如何操作...

完成前面的准备工作部分后,请遵循以下步骤:

  1. 将在准备工作部分下载的 google-services.json 文件复制到你的应用文件夹(<项目文件夹>\GoogleSignIn\app

  2. 打开项目 Gradle 构建文件:build.gradle (项目:GoogleSignIn),并在 buildscript dependencies 部分添加以下内容:

    classpath 'com.google.gms:google-services:1.5.0-beta2'
    
  3. 打开应用模块 Gradle 构建文件:build.gradle (模块:app),并在文件开头(android 部分之上)添加以下声明:

    apply plugin: 'com.google.gms.google-services'
    
  4. 在步骤 3 的同一模块构建文件中,将以下声明添加到依赖项部分:

    compile 'com.google.android.gms:play-services-auth:8.3.0'
    
  5. 打开activity_main.xml,用以下 XML 替换现有的TextView

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true" />
    
    <com.google.android.gms.common.SignInButton
        android:id="@+id/signInButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />
    
  6. 打开MainActivity.java并添加以下全局声明:

    private final int REQUEST_SIGN_IN=1;
    GoogleApiClient mGoogleApiClient;
    
  7. 添加以下OnConnectionFailedListener

    GoogleApiClient.OnConnectionFailedListener mOnConnectionFailedListener = new GoogleApiClient.OnConnectionFailedListener() {
        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {
            Toast.makeText(MainActivity.this, "connectionResult="+connectionResult.getErrorMessage(), Toast.LENGTH_SHORT).show();
        }
    };
    
  8. 在现有的onCreate()中添加以下代码:

    GoogleSignInOptions googleSignInOptions = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestEmail()
        .build();
    mGoogleApiClient = new GoogleApiClient.Builder(this)
        .addOnConnectionFailedListener(mOnConnectionFailedListener)
        .addConnectionCallbacks(mConnectionCallbacks)
        .addApi(Auth.GOOGLE_SIGN_IN_API, googleSignInOptions)
        .build();
    findViewById(R.id.signInButton).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            signIn();
        }
    });
    
  9. 按照以下步骤为onActivityResult()回调创建一个覆盖方法:

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_SIGN_IN) {
            GoogleSignInResult googleSignInResult = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
            if (googleSignInResult.isSuccess()) {
                GoogleSignInAccount googleSignInAccount = googleSignInResult.getSignInAccount();
                TextView textView = (TextView)findViewById(R.id.textView);
                textView.setText("Signed in: " + googleSignInAccount.getDisplayName());
                findViewById(R.id.signInButton).setVisibility(View.GONE);
            }
        }
    }
    
  10. 现在你可以将应用程序运行在设备或模拟器上了。

工作原理...

Google 使用他们的GoogleApiClientGoogleSignInOptions API 相对简单地添加了 Google 登录。首先,我们使用构建器创建一个GoogleSignInOptions对象。在这里,我们指定所需的登录选项,例如请求电子邮件 ID。然后,我们将其传递给GoogleApiClient构建器。

当用户点击使用com.google.android.gms.common.SignInButton类创建的 Google 登录按钮时,我们会向GoogleSignInApi发送一个 Intent。我们在onActivityResult()中处理结果。如果登录成功,我们可以获取账户详情。在我们的示例中,我们只获取电子邮件,但还有其他信息可用,例如:

  • getDisplayName(): 这是显示名称

  • getEmail(``): 电子邮件地址

  • getId(): Google 账户的唯一 ID

  • getPhotoUrl(): 显示照片

  • getIdToken(): 这是用于后端认证的

另请参阅部分查看GoogleSignInAccount链接以获取完整列表。

还有更多...

如果你想让你的应用程序能够被更广泛的受众使用,你需要考虑本地化。

本地化资源

Google 在 SDK 中提供了许多本地化字符串,位于此链接:<SDK 安装文件夹>/sdk/extras/google/google_play_services/libproject/google-play-services_lib/res/

另请参阅

第十五章:后端即服务选项

在本章中,我们将涵盖以下主题:

  • App42

  • Backendless

  • Buddy

  • Firebase

  • Kinvey

简介

当您的应用程序和用户基础增长时,您可能希望将应用程序跨设备甚至用户连接起来,例如高分排行榜。您有两个选择:

  • 创建和维护您自己的服务器

  • 使用后端即服务BaaS)提供商

作为移动开发者,创建和维护一个网站服务器是一个耗时的任务,可能会让您偏离开发工作。

备注

如果您不熟悉 BaaS 提供商,以下是一些背景信息:

维基百科——移动后端即服务:

en.wikipedia.org/wiki/Mobile_backend_as_a_service

我们将研究几个针对 Android 开发者的特定功能的 BaaS 提供商。仅包括提供原生 Android 支持和免费订阅的提供商。(仅提供免费试用或付费计划的提供商不包括在内。)当您的应用程序超出免费层级时,所有这些提供商都提供更高层级的服务,月费用各不相同。

下表提供了每个提供商每月免费服务的快速比较:

提供商月用户数API 调用推送通知文件存储
Firebase无限制100 SCN/A1 GB
Buddy*20/秒500 万10 GB
App42*每月 100 万100 万1 GB
Kinvey1000**30 GB
Backendless10050/秒100 万20 GB
  • = 他们的网站上没有发布

N/A = 功能不可用

SC = 同时连接数

备注

免责声明:前述表格和以下食谱的信息是从它们的公共网站获取的,可能会在它们的意愿下进行更改。如您所知,移动行业一直在变化;预计价格和服务会有所变动。请仅将此信息作为起点。

最后,这并不是一个详尽的 BaaS 提供商列表。希望这一章能提供一个关于 BaaS 能做什么以及如何为您的应用程序使用 BaaS 的好介绍。接下来的食谱将研究每个提供商,并带您了解将它们的库添加到您的项目的步骤。这将直接比较这些服务。正如您将看到的,一些服务比其他服务更容易使用,这可能是决定性因素。

App42

App42 是 ShepHertz 公司的 BaaS API 产品,提供包括游戏平台、平台即服务、营销分析在内的多种云服务。它们具有非常丰富的功能集,包括许多特别适用于游戏的服务。

App42 Android SDK 支持以下功能:

  • 用户服务

  • 存储服务

  • 自定义代码服务

  • 推送通知服务

  • 事件服务

  • 礼品管理服务

  • 定时服务

  • 社交服务

  • A/B 测试服务

  • Buddy 服务

  • 头像服务

  • 成就服务

  • 排行榜服务

  • 奖励服务

  • 上传服务

  • 图库服务

  • 地理服务

  • 会话服务

  • 评论服务

  • 购物车服务

  • 目录服务

  • 消息服务

  • 推荐服务

  • 邮件服务

  • 日志服务

注意

要注册 App42/ShepHertz,请访问以下链接:

apphq.shephertz.com/register

这是 App4 注册屏幕的截图:

App42

准备就绪

在 Android Studio 中创建一个名为 App42 的新项目。使用默认的 Phone & Tablet 选项,在选择 Activity Type 时选择 Empty Activity

从以下链接下载并解压 App42 SDK:

github.com/shephertz/App42_ANDROID_SDK/archive/master.zip

创建 App42 账户后(见前一个链接),登录到 AppHQ 管理控制台,并注册你的应用。你需要 ApiKey 和 SecretKey。

如何操作...

要向你的项目添加对 App42 的支持,首先打开 Android Manifest 文件,并按照以下步骤操作:

  1. 添加以下权限:

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
  2. 在你的文件浏览器中打开以下文件夹:<project folder>\App42\app\libs(如果 libs 文件夹不存在,请创建它),并将 App42_ANDROID-CAMPAIGN_x.x.jar 文件复制到 app\libs 文件夹。

  3. 打开 app 模块的 Gradle 构建文件:build.gradle (Module: app),并在 dependencies 部分添加以下内容:

    compile files('libs/App42_ANDROID-CAMPAIGN_x.x.jar')
    
  4. 打开 ActivityMain.java 并添加以下导入:

    import com.shephertz.app42.paas.sdk.android.App42API;
    
  5. onCreate() 回调中添加以下代码:

    App42API.initialize(this, "YOUR_API_KEY", "YOUR_SECRET_KEY");
    
  6. 你已经准备好在设备或模拟器上运行应用程序。

工作原理...

不幸的是,App42 不支持 Gradle 构建格式,因此你需要下载 JAR 文件并将其手动复制到 \libs 文件夹。

在第 3 步中,将 App42_ANDROID-CAMPAIGN_x.x.jar 中的 x.x 替换为你下载文件中的当前版本号。

在第 5 步中,用你注册 App42 应用时收到的凭证替换 YOUR_API_KEYYOUR_SECRET_KEY

还有更多...

下面是使用 App42 API 注册用户的示例:

UserService userService = App42API.buildUserService();
userService.createUser("userName", "password", "email", new App42CallBack() {
    public void onSuccess(Object response) {
        User user = (User)response;
        Log.i("UserService","userName is " + user.getUserName());
        Log.i("UserService", "emailId is " + user.getEmail());
    }
    public void onException(Exception ex) {
        System.out.println("Exception Message"+ex.getMessage());
    }
});

另请参阅

Backendless

除了MBaaS(即他们所说的移动后端即服务),Backendless 还提供其他服务,如托管、API 服务和市场。它们的 MBaaS 功能包括:

  • 用户管理

  • 数据持久化

  • 地理定位

  • 媒体流

  • 发布/订阅消息传递

  • 推送通知

  • 自定义业务逻辑

  • 分析

  • 移动代码生成

注意

要注册 Backendless,请点击此链接:

develop.backendless.com/#registration

这是 Backendless 注册窗口的截图:

Backendless

准备就绪

在 Android Studio 中创建一个新项目,命名为Backendless。使用默认的Phone & Tablet选项,并在提示Activity Type时选择Empty Activity

你需要一个Backendless账户(见前一个链接),并通过他们的Backendless控制台注册你的应用程序。获取到你的 App ID 和 Secret Key 后,开始以下步骤。

如何操作...

要将Backendless添加到你的项目中,打开 Android Manifest 并按照以下步骤操作:

  1. 添加以下权限:

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
  2. 打开 app 模块的 Gradle 构建文件:build.gradle (Module: app),并在dependencies部分添加以下内容:

    compile 'com.backendless:android:3.0.3'
    
  3. 打开ActivityMain.java并添加以下导入:

    import com.backendless.Backendless;
    
  4. onCreate()回调中添加以下代码:

    String appVersion = "v1";
    Backendless.initApp(this, YOUR_APP_ID, YOUR_SECRET_KEY, appVersion);
    
  5. 你准备好在设备或模拟器上运行应用程序。

工作原理...

在步骤 4 中,将YOUR_APP_IDYOUR_SECRET_KEY替换为你从Backendless控制台收到的凭证。

如果你更愿意直接下载 SDK 而不是使用 Maven 依赖,请在这里下载:backendless.com/sdk/java/3.0.0/backendless-sdk-android.zip

还有更多...

下面是使用BackendlessUser对象注册用户的示例:

BackendlessUser user = new BackendlessUser();
user.setEmail("<user@email>");
user.setPassword("<password>");
Backendless.UserService.register(user, new BackendlessCallback<BackendlessUser>() {
    @Override
    public void handleResponse(BackendlessUser backendlessUser) {
        Log.d("Registration", backendlessUser.getEmail() + " successfully registered");
    }
} );

另请参阅

Buddy

Buddy 与其他 BaaS 提供商略有不同,因为他们主要专注于连接设备和传感器。为了帮助维护隐私法规,Buddy 允许你选择将数据托管在美国或欧盟。

Buddy 支持以下常见场景:

  • 记录度量事件

  • 发送推送通知

  • 接收并安全存储遥测数据

  • 存储和管理二进制文件

  • 深入了解客户如何使用应用程序的移动分析

  • 将设备或应用程序数据与公司的 BI 系统整合

  • 在你选择的地理位置的沙盒私有数据。

如果你想要查看或贡献 Buddy SDK 的源代码,可以使用以下 Git 命令获取:

git clone https://github.com/BuddyPlatform/Buddy-Android-SDK.git

注意

要注册 Buddy,请点击以下链接:

www.buddyplatform.com/Signup

这是 Buddy 注册的截图:

Buddy

准备就绪

在 Android Studio 中创建一个新项目,命名为Buddy。使用默认的Phone & Tablet选项,并在提示Activity Type时选择Empty Activity

你需要一个 Buddy 账户(见前一个链接),并通过他们的仪表板注册你的应用程序。获取到你的 App ID 和 App Key 后,开始以下步骤。

如何操作...

要将 Buddy 添加到你的项目中,打开 Android Manifest 并按照以下步骤操作:

  1. 添加以下权限:

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    
  2. 打开 app 模块的 Gradle 构建文件:build.gradle (Module: app),并在dependencies部分添加以下内容:

    compile 'com.buddy:androidsdk:+'
    
  3. 打开ActivityMain.java并添加以下导入:

    import com.buddy.sdk.Buddy;
    
  4. onCreate()回调中添加以下代码:

    Buddy.init(myContext, "appId", "appKey");
    
  5. 你已经准备好在设备或模拟器上运行应用程序。

工作原理...

在第 4 步中,将appIdappKey替换为你在 Buddy 仪表盘中收到的凭据。

与其他大多数 BaaS 提供商类似,我们只需在 Gradle 构建中添加对 Maven 仓库的引用。然后,我们添加一个导入并开始调用 Buddy API。

还有更多...

下面是使用 Buddy 注册用户的示例:

Buddy.createUser("someUser", "somePassword", null, null, null, null, null, null, new BuddyCallback<User>(User.class) {
    @Override
    public void completed(BuddyResult<User> result) {
        if (result.getIsSuccess()) {
            Log.w(APP_LOG, "User created: " + result.getResult().userName);
        }
    }
});

另请参阅

  • 有关更多信息,请参考 Buddy 网页:buddy.com/

Firebase

Firebase 是一个主要关注数据库功能的 BaaS 提供商。虽然它们的功能不如其他大多数 BaaS 提供商全面,但它们确实擅长数据库。它们是此列表中唯一具有自动同步数据库功能的提供商。

Firebase 服务包括:

  • Firebase 实时数据库

  • Firebase 认证

  • Firebase 托管

  • 用户认证——电子邮件和密码、Facebook、Twitter、GitHub 和 Google

由于它们最近被谷歌收购,你可以期待与谷歌云解决方案的进一步整合,正如你在这个链接上看到的:

cloud.google.com/solutions/mobile/firebase-app-engine-android-studio

注意

要使用 Firebase 注册,请访问此链接:

www.firebase.com/login/

这是 Firebase 注册窗口的截图:

Firebase

准备就绪

在 Android Studio 中创建一个新项目,并将其命名为Firebase。使用默认的Phone & Tablet选项,在选择Activity Type时选择Empty Activity

你需要在你用 Firebase 注册应用程序时提供的 Firebase URL。

如何操作...

要将 Firebase 添加到你的项目中,首先打开 Android Manifest 并按照以下步骤操作:

  1. 添加以下权限:

    <uses-permission android:name="android.permission.INTERNET"/>
    
  2. 打开 app 模块的 Gradle 构建文件:build.gradle (Module: app),并在dependencies部分添加以下内容:

    compile 'com.firebase:firebase-client-android:2.5.0+'
    
  3. 打开ActivityMain.java并添加以下导入:

    import com.firebase.client.Firebase;
    
  4. onCreate()回调中添加以下代码:

    Firebase.setAndroidContext(this);
    Firebase firebase = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");
    
  5. 你已经准备好在设备或模拟器上运行应用程序。

工作原理...

将 Firebase 添加到你的应用程序中相当简单。将<YOUR-FIREBASE-APP>占位符替换为你在注册应用程序时 Firebase 提供的链接。

还有更多...

下面是使用 Firebase 注册用户的示例:

firebase.createUser("bobtony@firebase.com", "correcthorsebatterystaple", new Firebase.ValueResultHandler<Map<String, Object>>() {
    @Override
    public void onSuccess(Map<String, Object> result) {
        Log.i("Firebase", "Successfully created user account with uid: " + result.get("uid"));
    }
    @Override
    public void onError(FirebaseError firebaseError) {
        // there was an error
    }
});

另请参阅

Kinvey

Kinvey 是最早开始提供移动后端服务的提供商之一。他们的功能包括:

  • 用户管理

  • 数据存储

  • 文件存储

  • 推送通知

  • 社交网络集成

  • 位置服务

  • 生命周期管理

  • 版本控制

    注意

    console.kinvey.com/sign-up注册 Kinvey。

这是 Kinvey 注册窗口的截图:

Kinvey

准备工作

在 Android Studio 中创建一个新项目,并将其命名为Kinvey。使用默认的手机和平板选项,并在提示活动类型时选择空活动

从以下链接下载并解压 Kinvey SDK:download.kinvey.com/Android/kin…

你需要一个 Kinvey 账户(见前一个链接),并通过他们的开发者控制台注册你的应用程序。一旦你有 App Key 和 App Secret,就可以开始以下步骤。

如何操作...

要将 Kinvey 添加到你的项目中,请按照以下步骤操作:

  1. 在 Android Manifest 中添加以下权限:

    <uses-permission android:name="android.permission.INTERNET"/>
    
  2. 在你的文件浏览器中打开以下文件夹:<项目文件夹>\Kinvey\app\libs(如果libs文件夹不存在,请创建它),并将 SDK 的liblibJar文件夹中的所有文件复制到app\libs文件夹中。

  3. 打开 app 模块的 Gradle 构建文件:build.gradle (Module: app),并添加以下repositoriesdependencies(保留任何现有的条目):

    repositories {
        flatDir {
            dirs 'libs'
        }
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile(name:'kinvey-android-*', ext:'aar')
    }
    
  4. 打开MainActivity.java并添加以下导入:

    import com.kinvey.android.Client;
    
  5. 在类声明中添加以下内容:

    final Client mKinveyClient = new mKinveyClient("your_app_key", "your_app_secret", this.getApplicationContext()).build();
    
  6. 你现在可以在设备或模拟器上运行应用程序了。

工作原理...

Kinvey 不是最容易设置的 BaaS,因为它不提供简单的 Gradle 依赖项。相反,你需要像在第 2 步中那样,直接将他们的库添加到项目库中。

这些步骤将设置好 Kinvey 客户端,并准备开始向你的应用程序添加额外的功能。只需确保在 Kinvey 客户端构建器中用你的应用程序凭据替换占位符。

还有更多...

要验证你的设置是否正确,请在onCreate()方法中或在按钮点击时调用以下代码:

mKinveyClient.ping(new KinveyPingCallback() {
    public void onFailure(Throwable t) {
        Log.d("KinveyPingCallback", "Kinvey Ping Failed", t);
    }

    public void onSuccess(Boolean b) {
        Log.d("KinveyPingCallback", "Kinvey Ping Success");
    }
});

另请参阅