Android技术

$AppViewScreen全埋点方案

2019-05-01  本文已影响71人  Peakmain

参考书籍:《Android全埋点解决方案》

背景

$AppViewScreen事件,即页面浏览事件。在Android中页面浏览实际指的就是切换不同的Activity或者Fragment

权限6.0

使用

       if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_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) {
        AppViewScreenDataAPI.getInstance().ignoreAutoTrackActivity(MainActivity.class);
        switch (requestCode) {
            case PERMISSIONS_REQUEST_READ_CONTACTS:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // 用户点击允许
                } else {
                    // 用户点击禁止
                }
                break;
                default:
                    break;
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
    public static void requestPermissions(final @NonNull Activity activity,
            final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode) {
   //代码省略
        if (Build.VERSION.SDK_INT >= 23) {
            //实际会调用 onRequestPermissionsResult和Activity的startActivityForResult方法,随即会走activity的OnResume方法
            activity.requestPermissions(permissions, requestCode);
        } else if (activity instanceof OnRequestPermissionsResultCallback) {
           //代码省略
        }
    }
    public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
        if (mHasCurrentPermissionsRequest) {
           onRequestPermissionsResult(requestCode, new String[0], new int[0]);
            return;
        }
        Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
        startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
        mHasCurrentPermissionsRequest = true;
    }

用户无论点击同意还是拒绝实际都会调用 onRequestPermissionsResult和Activity的startActivityForResult方法,随即会走activity的OnResume方法,所以在埋点的时候需要避免多次调用onResume方法

实现方案

  private final String TAG = this.getClass().getSimpleName();
    public static final String SDK_VERSION = "1.0.0";
    private static AppViewScreenDataAPI INSTANCE;
    private static final Object mLock = new Object();
    private static Map<String, Object> mDeviceInfo;
    private String mDeviceId;


    private AppViewScreenDataAPI(Application application) {
        mDeviceId = AppViewScreenDataPrivate.getAndroidID(application.getApplicationContext());
        mDeviceInfo = AppViewScreenDataPrivate.getDeviceInfo(application.getApplicationContext());
        AppViewScreenDataPrivate.registerActivityLifecycleCallbacks(application);
    }

    @Keep
    public static AppViewScreenDataAPI init(Application application) {
        if (INSTANCE == null) {
            synchronized (mLock) {
                if (null == INSTANCE) {
                    INSTANCE = new AppViewScreenDataAPI(application);
                }
            }
        }
        return INSTANCE;
    }

    @Keep
    public static AppViewScreenDataAPI getInstance() {
        return INSTANCE;
    }

指定不采集哪个 Activity 的页面浏览事件

    public void ignoreAutoTrackActivity(Class<?> activity) {
        AppViewScreenDataPrivate.ignoreAutoTrackActivity(activity);
    }

恢复采集某个 Activity 的页面浏览事件

   public void removeIgnoredActivity(Class<?> activity) {
        AppViewScreenDataPrivate.removeIgnoredActivity(activity);
    }

事件

    /**
     * track 事件
     *
     * @param eventName  String 事件名称
     * @param properties JSONObject 事件自定义属性
     */
    public void track(@NonNull String eventName, @Nullable JSONObject properties) {
        try {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("event", eventName);
            jsonObject.put("device_id", mDeviceId);

            JSONObject sendProperties = new JSONObject(mDeviceInfo);

            if (properties != null) {
                AppViewScreenDataPrivate.mergeJSONObject(properties, sendProperties);
            }

            jsonObject.put("properties", sendProperties);
            jsonObject.put("time", System.currentTimeMillis());
            //打印数据
            Log.i(TAG, AppViewScreenDataPrivate.formatJson(jsonObject.toString()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static List<String> mIgnoredActivities;

    static {
        mIgnoredActivities = new ArrayList<>();
    }

    private static final SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"
            + ".SSS", Locale.CHINA);

指定不采集哪个 Activity 的页面浏览事件

    public static void ignoreAutoTrackActivity(Class<?> activity) {
        if (activity == null) {
            return;
        }
        mIgnoredActivities.add(activity.getClass().getCanonicalName());
    }

恢复采集某个 Activity 的页面浏览事件

    public static void removeIgnoredActivity(Class<?> activity) {
        if (activity == null) {
            return;
        }

        if (mIgnoredActivities.contains(activity.getClass().getCanonicalName())) {
            mIgnoredActivities.remove(activity.getClass().getCanonicalName());
        }
    }

获取 Android ID

    public static String getAndroidID(Context mContext) {
        String androidID = "";
        try {
            androidID = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.ANDROID_ID);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return androidID;
    }

获取设备信息

    public static Map<String, Object> getDeviceInfo(Context context) {
        final Map<String, Object> deviceInfo = new HashMap<>();
        deviceInfo.put("$lib", "Android");
        deviceInfo.put("$lib_version", AppViewScreenDataAPI.SDK_VERSION);
        deviceInfo.put("$os", "Android");
        deviceInfo.put("$os_version",
                Build.VERSION.RELEASE == null ? "UNKNOWN" : Build.VERSION.RELEASE);
        deviceInfo
                .put("$manufacturer", Build.MANUFACTURER == null ? "UNKNOWN" : Build.MANUFACTURER);
        if (TextUtils.isEmpty(Build.MODEL)) {
            deviceInfo.put("$model", "UNKNOWN");
        } else {
            deviceInfo.put("$model", Build.MODEL.trim());
        }
        try {
            final PackageManager manager = context.getPackageManager();
            final PackageInfo packageInfo = manager.getPackageInfo(context.getPackageName(), 0);
            deviceInfo.put("$app_version", packageInfo.versionName);

            int labelRes = packageInfo.applicationInfo.labelRes;
            deviceInfo.put("$app_name", context.getResources().getString(labelRes));
        } catch (final Exception e) {
            e.printStackTrace();
        }

        final DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
        deviceInfo.put("$screen_height", displayMetrics.heightPixels);
        deviceInfo.put("$screen_width", displayMetrics.widthPixels);

        return Collections.unmodifiableMap(deviceInfo);
    }

获取activity的标题

    private static String getToolbarTitle(Activity activity) {
        ActionBar actionBar = activity.getActionBar();
        if (actionBar != null) {
            if (!TextUtils.isEmpty(actionBar.getTitle())) {
                return actionBar.getTitle().toString();
            }
        } else {
            if (activity instanceof AppCompatActivity) {
                AppCompatActivity appCompatActivity = (AppCompatActivity) activity;
                android.support.v7.app.ActionBar supportActionBar = appCompatActivity.getSupportActionBar();
                if (supportActionBar != null) {
                    if (!TextUtils.isEmpty(supportActionBar.getTitle())) {
                        return supportActionBar.getTitle().toString();
                    }
                }
            }
        }
        return null;
    }

    /**
     * 获取 Activity 的 title
     */
    @SuppressWarnings("all")
    private static String getActivityTitle(Activity activity) {
        String activityTitle = "";
        if (activity == null) {
            return null;
        }
        try {
            activityTitle = activity.getTitle().toString();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                String toolbarTitle = getToolbarTitle(activity);
                if (!TextUtils.isEmpty(toolbarTitle)) {
                    activityTitle = toolbarTitle;
                }
            }
            if (TextUtils.isEmpty(activityTitle)) {
                PackageManager packageManager = activity.getPackageManager();
                if (packageManager != null) {
                    ActivityInfo activityInfo = packageManager.getActivityInfo(activity.getComponentName(), 0);
                    if (activityInfo != null) {
                        activityTitle = activityInfo.loadLabel(packageManager).toString();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return activityTitle;
    }

注册 Application.ActivityLifecycleCallbacks

    /**
     * 注册 Application.ActivityLifecycleCallbacks
     */
    public static void registerActivityLifecycleCallbacks(Application application) {
        application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

            }

            @Override
            public void onActivityStarted(Activity activity) {

            }

            @Override
            public void onActivityResumed(Activity activity) {
                trackAppViewScreen(activity);
            }

            @Override
            public void onActivityPaused(Activity activity) {

            }

            @Override
            public void onActivityStopped(Activity activity) {

            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }

Track 页面浏览事件

    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());
            properties.put("$title", getActivityTitle(activity));
            AppViewScreenDataAPI.getInstance().track("$AppViewScreen", properties);
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

拼接起来

    private static void addIndentBlank(StringBuilder sb, int indent) {
        try {
            for (int i = 0; i < indent; i++) {
                sb.append('\t');
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String formatJson(String jsonStr) {
        try {
            if (null == jsonStr || "".equals(jsonStr)) {
                return "";
            }
            StringBuilder sb = new StringBuilder();
            char last;
            char current = '\0';
            int indent = 0;
            boolean isInQuotationMarks = false;
            for (int i = 0; i < jsonStr.length(); i++) {
                last = current;
                current = jsonStr.charAt(i);
                switch (current) {
                    case '"':
                        if (last != '\\') {
                            isInQuotationMarks = !isInQuotationMarks;
                        }
                        sb.append(current);
                        break;
                    case '{':
                    case '[':
                        sb.append(current);
                        if (!isInQuotationMarks) {
                            sb.append('\n');
                            indent++;
                            addIndentBlank(sb, indent);
                        }
                        break;
                    case '}':
                    case ']':
                        if (!isInQuotationMarks) {
                            sb.append('\n');
                            indent--;
                            addIndentBlank(sb, indent);
                        }
                        sb.append(current);
                        break;
                    case ',':
                        sb.append(current);
                        if (last != '\\' && !isInQuotationMarks) {
                            sb.append('\n');
                            addIndentBlank(sb, indent);
                        }
                        break;
                    default:
                        sb.append(current);
                }
            }

            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读