$AppStart、$AppEnd全埋点方案

2019-05-10  本文已影响0人  Peakmain

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

源码:https://github.com/Peakmain/baselibrary

背景

原理

一、应用程序如果有多个进程该如何判断是处于前台还是后台

二、应用程序如果发生崩溃或者被强杀死该如何判断该应用处于前台还是后台

思路

代码实现

AppViewScreenDataPrivate代码修改
首先我们需要一些变量。 时间:30s,倒计时:CountDownTimer,当前activity:WeakReference<Activity>,一个帮助类

    private static SensorsDatabaseHelper mDatabaseHelper;
    /**
     * 当前activity的集合
     */
    private static WeakReference<Activity> mCurrentActivity;
    //倒计时
    private static CountDownTimer countDownTimer;
    //30s
    private final static int SESSION_INTERVAL_TIME = 30 * 1000;

修改Application.ActivityLifecycleCallbacks

public static void registerActivityLifecycleCallbacks(Application application) {
        mDatabaseHelper=new SensorsDatabaseHelper(application.getApplicationContext(),application.getPackageName());
        //初始化30s,间隔1s
        countDownTimer=new CountDownTimer(SESSION_INTERVAL_TIME,10*1000) {
            @Override
            public void onTick(long millisUntilFinished) {

            }

            @Override
            public void onFinish() {
                trackAppEnd(mCurrentActivity.get());
            }
        };
        application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

            }

            @Override
            public void onActivityStarted(Activity activity) {
                //添加标记,已经开始了
                mDatabaseHelper.commitAppStart(true);
                //获得间隔时间
                double timeDiff = System.currentTimeMillis() - mDatabaseHelper.getAppPausedTime();
                //如果间隔时间大于30s
                if (timeDiff > SESSION_INTERVAL_TIME) {
                    //判断是否已经触发过了$AppEnd
                    if (!mDatabaseHelper.getAppEndEventState()) {
                        trackAppEnd(activity);
                    }
                }
                if (mDatabaseHelper.getAppEndEventState()) {
                    //设置未触发$AppEnd
                    mDatabaseHelper.commitAppEndEventState(false);
                    trackAppStart(activity);
                }
            }

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

            @Override
            public void onActivityPaused(Activity activity) {
                mCurrentActivity = new WeakReference<>(activity);
                countDownTimer.start();
                mDatabaseHelper.commitAppPausedTime(System.currentTimeMillis());
            }

            @Override
            public void onActivityStopped(Activity activity) {

            }

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

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
    }

AppStart和AppEnd事件

    /**
     * $AppStart事件
     */
    private static void trackAppStart(Activity activity) {
        try {
            if (activity == null) {
                return;
            }
            JSONObject properties = new JSONObject();
            properties.put("$activity", activity.getClass().getCanonicalName());
            properties.put("$title", getActivityTitle(activity));
            AppViewScreenDataAPI.getInstance().track("$AppStart", properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * $AppEnd事件
     */
    private static void trackAppEnd(Activity activity) {
        try {
            if(activity==null){
                return;
            }
            JSONObject properties=new JSONObject();
            properties.put("$activity",activity.getClass().getCanonicalName());
            properties.put("$title",getActivityTitle(activity));
            AppViewScreenDataAPI.getInstance().track("$AppEnd", properties);
            mDatabaseHelper.commitAppEndEventState(true);
            mCurrentActivity = null;
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

注册 AppStart 的监听:当appstart的时候,需要去取消上个页面退出时启动的倒计时

    public static void registerActivityStateObserver(Application application) {
        application.getContentResolver().registerContentObserver(mDatabaseHelper.getAppStartUri(),
                false, new ContentObserver(new Handler()) {
                    @Override
                    public void onChange(boolean selfChange, Uri uri) {
                        if (mDatabaseHelper.getAppStartUri().equals(uri)) {
                            countDownTimer.cancel();
                        }
                    }
                });
    }

SensorsDatabaseHelper帮助类

public class SensorsDatabaseHelper {
    private static final String SENSORSDATACONTENTPROVIDER = ".SensorsDataContentProvider/";
    //内容解析者
    private ContentResolver mContentResolver;
    //uri
    private Uri mAppStart;
    private Uri mAppEndState;
    private Uri mAppPausedTime;

    public static final String APP_STARTED = "$app_started";
    public static final String APP_END_STATE = "$app_end_state";
    public static final String APP_PAUSED_TIME = "$app_paused_time";

    public SensorsDatabaseHelper(Context context, String packageName) {
        mContentResolver = context.getContentResolver();
        mAppStart = Uri.parse("content://" + packageName + SENSORSDATACONTENTPROVIDER + SensorsDataTable.APP_STARTED.getName());
        mAppEndState = Uri.parse("content://" + packageName + SENSORSDATACONTENTPROVIDER + SensorsDataTable.APP_END_STATE.getName());
        mAppPausedTime = Uri.parse("content://" + packageName + SENSORSDATACONTENTPROVIDER + SensorsDataTable.APP_PAUSED_TIME.getName());

    }
    /**
     * 添加标记已经开始了,添加到sharedpreferences
     *
     * @param appStart true 表示开始了
     */
    public void commitAppStart(boolean appStart) {
        ContentValues values=new ContentValues();
        values.put(APP_STARTED,appStart);
        mContentResolver.insert(mAppStart,values);
    }

    /**
     * 将暂停时间  添加到sharedpreferences
     *
     * @param pausedTime 暂停时间
     */
    public void commitAppPausedTime(long pausedTime) {
        ContentValues contentValues = new ContentValues();
        contentValues.put(APP_PAUSED_TIME, pausedTime);
        mContentResolver.insert(mAppPausedTime, contentValues);
    }

    /**
     * 获得 暂停时间
     */
    public long getAppPausedTime() {
        long pasuedTime = 0;
        Cursor cursor = mContentResolver.query(mAppPausedTime, new String[]{APP_PAUSED_TIME}, null, null, null);
        if (cursor != null && cursor.getCount() > 0) {
            while (cursor.moveToNext()) {
                pasuedTime = cursor.getLong(0);
            }
        }
        if (cursor != null) {
            cursor.close();
        }
        return pasuedTime;
    }

    /**
     * 保存状态到sp
     * @param appEndState
     */
    public void commitAppEndEventState(boolean appEndState) {
        ContentValues contentValues=new ContentValues();
        contentValues.put(APP_END_STATE,appEndState);
        mContentResolver.insert(mAppEndState,contentValues);
    }

    /**
     * app是否已经触发过$AppEnd事件
     */
    public boolean getAppEndEventState() {
        boolean state = true;
        Cursor cursor = mContentResolver.query(mAppEndState, new String[]{APP_END_STATE}, null, null, null);
        if (cursor != null && cursor.getCount() > 0) {
            while (cursor.moveToNext()) {
                state = cursor.getInt(0) > 0;
            }
        }

        if (cursor != null) {
            cursor.close();
        }
        return state;
    }
    public Uri getAppStartUri() {
        return mAppStart;
    }
}

SensorsDataContentProvider

public class SensorsDataContentProvider extends ContentProvider{
    //id
    private final static int APP_START = 1;
    private final static int APP_END_STATE = 2;
    private final static int APP_PAUSED_TIME = 3;

    private static SharedPreferences sharedPreferences;
    private static SharedPreferences.Editor mEditor;
    private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private ContentResolver mContentResolver;
    @Override
    public boolean onCreate() {
        if(getContext()!=null){
            String packageName = getContext().getPackageName();
            //添加
            uriMatcher.addURI(packageName + ".SensorsDataContentProvider", SensorsDataTable.APP_STARTED.getName(), APP_START);
            uriMatcher.addURI(packageName + ".SensorsDataContentProvider", SensorsDataTable.APP_END_STATE.getName(), APP_END_STATE);
            uriMatcher.addURI(packageName + ".SensorsDataContentProvider", SensorsDataTable.APP_PAUSED_TIME.getName(), APP_PAUSED_TIME);
            sharedPreferences = getContext().getSharedPreferences("com.peakmain.sdk.AppViewScreenDataAPI", Context.MODE_PRIVATE);
            mEditor = sharedPreferences.edit();
            mEditor.apply();
            mContentResolver = getContext().getContentResolver();
        }
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        int code=uriMatcher.match(uri);
        //没有数据库使用MatrixCursor替代
        MatrixCursor matrixCursor=null;
        switch (code) {
            case APP_START:
                int appStart = sharedPreferences.getBoolean(SensorsDatabaseHelper.APP_STARTED, true) ? 1 : 0;
                matrixCursor = new MatrixCursor(new String[]{SensorsDatabaseHelper.APP_STARTED});
                matrixCursor.addRow(new Object[]{appStart});
                break;
            case APP_END_STATE:
                int appEnd = sharedPreferences.getBoolean(SensorsDatabaseHelper.APP_END_STATE, true) ? 1 : 0;
                matrixCursor = new MatrixCursor(new String[]{SensorsDatabaseHelper.APP_END_STATE});
                matrixCursor.addRow(new Object[]{appEnd});
                break;
            case APP_PAUSED_TIME:
                long pausedTime = sharedPreferences.getLong(SensorsDatabaseHelper.APP_PAUSED_TIME, 0);
                matrixCursor = new MatrixCursor(new String[]{SensorsDatabaseHelper.APP_PAUSED_TIME});
                matrixCursor.addRow(new Object[]{pausedTime});
                break;
            default:
                break;
        }
        return matrixCursor;
    }
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        if (values == null) {
            return uri;
        }
        int code = uriMatcher.match(uri);
        switch (code) {
            case APP_START:
                boolean appStart = values.getAsBoolean(SensorsDatabaseHelper.APP_STARTED);
                mEditor.putBoolean(SensorsDatabaseHelper.APP_STARTED, appStart);
                mContentResolver.notifyChange(uri, null);
                break;
            case APP_END_STATE:
                boolean appEnd = values.getAsBoolean(SensorsDatabaseHelper.APP_END_STATE);
                mEditor.putBoolean(SensorsDatabaseHelper.APP_END_STATE, appEnd);
                break;
            case APP_PAUSED_TIME:
                long pausedTime = values.getAsLong(SensorsDatabaseHelper.APP_PAUSED_TIME);
                mEditor.putLong(SensorsDatabaseHelper.APP_PAUSED_TIME, pausedTime);
                break;
            default:
                break;
        }
        mEditor.commit();
        return uri;
    }
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }



    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}

需要记得注册

    <application>
        <provider
            android:name=".datacollection.appstartandend.SensorsDataContentProvider"
            android:authorities="${applicationId}.SensorsDataContentProvider"
            android:enabled="true"
            android:exported="false" />
    </application>
上一篇下一篇

猜你喜欢

热点阅读