- Android全埋点解决方案
- 王灼洲
- 1550字
- 2023-07-19 17:42:49
2.4 完善方案
在Android 6.0(API 23)发布的同时又引入了一种新的权限机制,即Runtime Permissions,又称运行时权限。
在一般情况下,我们如果要使用Runtime Permissions主要分为四个步骤,下面我们以使用(申请)“android.permission.READ_CONTACTS”权限为例来介绍。
第1步:声明权限
需要在AndroidManifest.xml文件中使用uses-permission声明应用程序要使用的权限列表。
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.sensorsdata.analytics.android.app"> <uses-permission android:name="android.permission.READ_CONTACTS" /> <application android:name=".MyApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
第2步:检查权限
如果应用程序需要使用READ_CONTACTS权限,则要在每次真正使用READ_CONTACTS权限之前,检测当前应用程序是否已经拥有该权限,这是因为用户可能随时会在Android系统的设置中关掉授予当前应用程序的任何权限。检测权限可以使用ContextCompat的checkSelfPermission方法,简单示例如下:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { //拥有权限 } else { //没有权限,需要申请权限 }
其中,PackageManager.PERMISSION_GRANTED代表当前应用程序已经拥有了该权限;反之,PackageManager.PERMISSION_DENIED代表当前应用程序没有获得该权限,需要再次申请。
第3步:申请权限
可以通过调用ActivityCompat的requestPermissions方法来申请一个或者一组权限,简单示例如下:
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, PERMISSIONS_REQUEST_READ_CONTACTS);
调用ActivityCompat.requestPermissions方法之后,系统会弹出如图2-2的请求权限对话框(该对话框可能会随着ROM的不同而略有差异):
图2-2 请求权限提示框
第4步:处理权限请求结果
用户选择之后的结果会回调当前Activity的onRequestPermissionsResult(int requestCode,@NonNull String[]permissions,@NonNull int[]grantResults)方法,我们可以根据requestCode和grantResults参数来判断用户选择了“允许”还是“禁止”按钮。
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case PERMISSIONS_REQUEST_READ_CONTACTS: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //用户点击允许 } else { //用户点击禁止 } break; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); }
讲到这里,你肯定开始疑惑了,这跟采集页面浏览事件有什么关系呢?
其实是有关系的!我们继续往下看。
通过测试可以发现,我们调用ActivityCompat.requestPermissions方法申请权限之后,不管用户选择了“允许”还是“禁止”按钮,系统都会先调用onRequestPermissionsResult回调方法,然后再调用当前Activity的onResume生命周期函数。而我们上面介绍的,就是通过onResume生命周期函数来采集页面浏览事件的,这个现象会直接导致我们的埋点SDK再一次触发页面浏览事件。
对于这个问题,我们该如何解决呢?事实上,虽然目前也没有非常完美的解决方案,但是我们还是可以借助其他方法来尝试解决。毕竟,在一个完整的应用程序中,真正需要申请权限的页面并不是很多。所以,我们可以在这些申请权限的页面里进行一些特殊的“操作”来规避上面的问题。
我们可以考虑给埋点SDK新增一个功能,即用户可以设置想要过滤哪些Activity的页面浏览事件(即指定不采集哪些Activity的页面浏览事件),然后通过灵活使用这个接口,解决上面的问题。
下面我们详细地介绍一下具体的实现步骤。
第1步:在SensorsDataAPI中新增两个接口
package com.sensorsdata.analytics.android.sdk; import android.app.Application; import android.support.annotation.Keep; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; import org.json.JSONObject; import java.util.Map; /** * Created by 王灼洲 on 2018/7/22 */ @Keep public class SensorsDataAPI { ...... /** *指定不采集哪个 Activity 的页面浏览事件 * * @param activity Activity */ public void ignoreAutoTrackActivity(Class<?> activity) { SensorsDataPrivate.ignoreAutoTrackActivity(activity); } /** * 恢复采集某个 Activity 的页面浏览事件 * * @param activity Activity */ public void removeIgnoredActivity(Class<?> activity) { SensorsDataPrivate.removeIgnoredActivity(activity); } ...... }
·ignoreAutoTrackActivity(Class<?>activity)
指定忽略采集哪个Activity的页面浏览事件。
·removeIgnoredActivity(Class<?>activity)
指定恢复采集哪个Activity的页面浏览事件。
以上两个接口,都是调用私有类SensorsDataPrivate中相对应的方法。
package com.sensorsdata.analytics.android.sdk; ...... /*public*/ class SensorsDataPrivate { private static List<String> mIgnoredActivities; static { mIgnoredActivities = new ArrayList<>(); } public static void ignoreAutoTrackActivity(Class<?> activity) { if (activity == null) { return; } mIgnoredActivities.add(activity.getClass().getCanonicalName()); } public static void removeIgnoredActivity(Class<?> activity) { if (activity == null) { return; } if (mIgnoredActivities.contains(activity.getClass().getCanonicalName())) { mIgnoredActivities.remove(activity.getClass().getCanonicalName()); } } ...... }
内部实现机制比较简单,仅仅通过定义一个List来保存忽略采集页面浏览事件的Activity的名称(包名+类名)。
第2步:修改trackAppViewScreen(Activity activity)方法添加相应的判断逻辑
/** * Track 页面浏览事件 * * @param activity Activity */ @Keep private static void trackAppViewScreen(Activity activity) { try { if (activity == null) { return; } if (mIgnoredActivities.contains(activity.getClass().getCanonicalName())) { return; } JSONObject properties = new JSONObject(); properties.put("$activity", activity.getClass().getCanonicalName()); SensorsDataAPI.getInstance().track("$AppViewScreen", properties); } catch (Exception e) { e.printStackTrace(); } }
首先判断当前Activity是否已经被忽略,如果被忽略,则不触发页面浏览事件,否则将触发页面浏览事件。
第3步:修改申请权限的Activity
在申请权限的Activity中,在它的onRequestPermissionsResult回调中首先调用ignoreAutoTrackActivity方法来忽略当前Activity的页面浏览事件,然后在onStop生命周期函数中恢复采集当前Activity的页面浏览事件。
package com.sensorsdata.analytics.android.app; import android.Manifest; import android.content.pm.PackageManager; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import com.sensorsdata.analytics.android.sdk.SensorsDataAPI; public class MainActivity extends AppCompatActivity { private final static int PERMISSIONS_REQUEST_READ_CONTACTS = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setTitle("Home"); if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { //拥有权限 } else { //没有权限,需要申请全新啊 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission. READ_CONTACTS}, PERMISSIONS_REQUEST_READ_CONTACTS); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { SensorsDataAPI.getInstance().ignoreAutoTrackActivity(MainActivity.class); switch (requestCode) { case PERMISSIONS_REQUEST_READ_CONTACTS: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 用户点击允许 } else { // 用户点击禁止 } break; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); } @Override protected void onStop() { super.onStop(); SensorsDataAPI.getInstance().removeIgnoredActivity(MainActivity.class); } }
这样处理之后,就可以解决申请权限再次触发页面浏览事件的问题了。