《Android全埋点解决方案》 —2.4 完善方案

举报
华章计算机 发表于 2019/12/19 11:49:33 2019/12/19
【摘要】 本节书摘来自华章计算机《Android全埋点解决方案》 一书中第2章,第2.4节,作者是王灼洲 。

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的不同而略有差异):

 image.png

图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);

    }

}

这样处理之后,就可以解决申请权限再次触发页面浏览事件的问题了。


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。