app sdk开发

1,287 阅读1分钟

记录一下对于app的sdk的开发

为了能够在app端记录用户的访问轨迹和行为,我们需要进行埋点,也需要开发属于项目的sdk,分享一下开发的思路、代码以及很多有用的集成方法。

思路

和微信的全面sdk不同,我们的sdk就是集成的共需方法,用于统计用户的各种所需要统计的行为,为了兼容各个版本或者项目,所以代码书写都需要考虑兼容性,所以基本都是基于原生代码。

(function(window) {
  SDK = {};
  var sTime = _sTime || new Date();

  // 默认参数
  var df = {
  };
  
   // 功能函数
  var skill = {
      gStayTime: function() {
          var under = new Date();
          return under - sTime;
      }
  }

  // 一些工具函数
  var common = {
      cookie: {
          get: function(name) {
              return (name = RegExp("(^| )" + name + "=([^;]*)(;|$)").exec(document.cookie)) ? name[2] : null
          },
          /**
           * 设置 cookie
           * @param {string} name cookie name
           * @param {string} key  cookie value
           * @param {option} opt  {expire: 1000, domain: '', path: '', Ra: true}
           */
          set: function(name, key, opt) {
              var expireTime;
              opt.expire && (expireTime = new Date, expireTime.setTime(expireTime.getTime() + opt.expire));
              document.cookie = name + "=" + key
                  + (opt.domain ? "; domain=" + opt.domain: "")
                  + (opt.path ? "; path=" + opt.path: "")
                  + (expireTime ? "; expires=" + expireTime.toGMTString() : "")
                  + (opt.Ra ? "; secure": "")
                  ;
          }
      },
      localStorage: {
          check: function() {
          },
          set: function(name, val, expire) {
          },
          get: function() {},
          remove: function() {}
      },
      sessionStorage: {
          get: function() {},
          set: function() {}
      },
      event: {
          add: function(elem, name, listener) {
              if (window.addEventListener) {
                  elem.addEventListener(name, listener, false);
              }
              else if (window.attachEvent) {
                  elem.attachEvent('on' + name, listener);
              }
              else {
                  elem['on' + name] = listener;
              }
          },
          del: function(elem, name, listener) {
              if (elem.removeEventListener) {
                  elem.removeEventListener(name, listener, false);
              } else if (elem,detachEvent) {
                  elem.detachEvent('on' + name, listener);
              } else {
                  elem['on' + name] = null;
              }
          }
      },
      lang: {
          checkType: function() {},
          checkNumber: function() {},
          checkString: function() {}
      },
      send: {
          encodeURI: function(url) {
              return escape(url).replace(/\+/g, '%2B').replace(/\"/g,'%22').replace(/\'/g, '%27').replace(/\//g,'%2F');
          },
          log: function(url, cb, params) {
              var logImg = new Image;
              var randomT = (new Date()).getTime() % ( 3600 * 1000);
              rName = 'logFromJS' + Math.floor(2147483648 * Math.random()).toString(36) + randomT;
              window[rName] = logImg;
              logImg.onload = logImg.onerror = logImg.onabort = function() {
                  logImg.onload = logImg.onerror = logImg.onabort = null;
                  logImg = window[rName] = null;
                  cb && cb(url);
              };
              // 若有参数,添加参数
              var urlParams = [];
              var postData = '';
              if (params) {
                  for (key in params) {
                      if (Object.prototype.hasOwnProperty.call(params, key) === true) {
                          urlParams.push('' + key + '=' + encodeURI(params[key]));
                      }
                  }
              }
              urlParams.push('random=' + rName);
              postData = urlParams.join('&');
              url = postData === '' ? url : (url + '?' + postData);
              logImg.src = url;
          },
          /**
           * 封装 ajax 
           * @param  {obj} opt .method.url.async.data.success
           * @return {null}     
           */
          ajax: function(opt) {
              opt = opt || {};
              opt.method = opt.method.toUpperCase() || 'POST';
              opt.url = opt.url || '';
              opt.async = opt.async || true;
              opt.data = opt.data || null;
              opt.success = opt.success || function() {};

              var xmlhttp;
              if (window.XMLHttpRequest) {
                  xmlhttp = new XMLHttpRequest();
              } else {
                  xmlhttp = new ActiveXObject('microsoft.XMLHTTP');
              }

              var params = [];
              var key;
              for (key in opt.data) {
                  if (Object.prototype.hasOwnProperty(opt.data, key) === true) {
                      params.push(key + '=' + opt.data[key]);
                  }
              }
              var postData = params.join('&');

              if (opt.method.toUpperCase() === 'POST') {
                  xmlhttp.open(opt.method, opt.url, opt.async);
                  xmlhttp.setRequestHeader('Content-Type', 'application/json');
                  xmlhttp.send(postData);
              } else if (opt.method.toUpperCase() === 'GET') {
                  xmlhttp.open(opt.method, opt.url + '?' + postData, opt.async);
                  xmlhttp.send(null);
              }

              xmlhttp.onreadystatechange = function() {
                  if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
                      opt.success(xmlhttp.responseText);
                  }
              }
          }
      },
      url: {
          gUrlParm: function() {},
          gProtocol: function() {},
          gDomain: function() {},
          gDomainNoPort: function() {},
          gUrlPathname: function() {}
      }
  }

  /**
   * 浏览器加载完成时执行对应的函数
   * @param  {function}  待执行函数
   */
  var domready = (function () {
      var fns = [], listener
          , doc = document
          , hack = doc.documentElement.doScroll
          , domContentLoaded = 'DOMContentLoaded'
          , loaded = (hack ? /^loaded|^c/ : /^loaded|^i|^c/).test(doc.readyState)
          ;
      if (!loaded) {
          common.event.add(doc, domContentLoaded, listener = function() {
              common.event.del(doc, domContentLoaded, listener);
              loaded = 1;
              while(listener = fns.shift()) listener()
          })
          common.event.add(window, 'load', listener = function() {
              loaded = 1;
              while(listener = fns.shift()) listener();
          })
      }

      return function (fn) {
          loaded ? setTimeout(fn, 0) : fns.push(fn)
      }
  })()

  SDK.skill = skill
  SDK.common = common;
  SDK.domready = domready;

  ;(function() {
      SDK.domready(function() {
          // 获取cookie, 若存在,更新expires, 否则重新计算唯一标识然后存储
          var ud = SDK.common.cookie.get('ud');
          if (ud) {
              SDK.common.cookie.set('ud', ud, {expire: 365 * 24 * 60 * 60 * 1000});
              SDK.df.ud = ud;
          } else {
              var finger = new Date();
              new Fingerprint2().get(function(result, components) {
                  console.log((new Date()).getTime() - finger);
                  var oldUd = SDK.common.cookie.get('ud');

                  SDK.common.cookie.set('ud', result, {expire: 365 * 24 * 60 * 60 * 1000});
                  SDK.df.ud = result;
              })
          }

          // 获取 uuid, 若存在, 存储到df, 否则重新计算一个 uuid
          var ussid = SDK.common.cookie.get('ussid');
          if (ussid) {
              console.log('ussid cookie 还保存');
              SDK.df.ussid = ussid;
          } else {
              console.log('ussid cookie 失效');
              var new_ussid = createUUID() + '$' + (new Date()).getTime()
              SDK.common.cookie.set('ussid', new_ussid, {});
              SDK.df.ussid = new_ussid;
          }

          // 页面关闭时
          SDK.common.event.add(window, 'unload', function() { sendInfo({ at: 3 }); });

          // 心跳计时器
          function checkWindow() {
              clearTimeout(timeHandle);
              var ifVisible;
              var stayTime = SDK.skill.gStayTime();
              visiAttr && (ifVisible = 'visible' == document[visiAttr]);
              hiddenAttr && (ifVisible = !document[hiddenAttr]);

              if (ifVisible !== visibleState) {
                  visibleState = ifVisible;
              }
              if (stayTime <= 3600000) {
                  sendInfo({ at: 2 });
              }
              timeHandle = setTimeout(checkWindow, 5 * 1000);
          }
          // 兼容属性名称
          function findAttr(attr) {
              var doc = document;
              l = '';
              if (attr in doc) l = attr;
              else {
                  for (var tmp = ['webkit', 'ms', 'moz', 'o'], index = 0; index < tmp.length; index++) {
                      var tmpAttr = tmp[index] + attr.charAt(0).toUpperCase() + attr.slice(1);
                      if (tmpAttr in doc) {
                          l = tmpAttr;
                          break;
                      }
                  }
              }
              return l;
          }
          // 设置指纹并发送加载完成事件
          function startHeart(ud) {
              SDK.common.cookie.set('ud', ud, {expire: 365 * 24 * 60 * 60 * 1000});
              SDK.df.ud = ud;
              sendInfo({ at: 1 });
              checkWindow();
          }

          function createUUID() {
              var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx'.replace(/[xy]/g, function(c) {
                  var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
                  return v.toString(16);
              });
              return uuid.split('-').join('');
          }
          var timeHandle,
              visiAttr = findAttr('visibilityState'),
              hiddenAttr = findAttr('hidden'),
              visibleState = false,
              beginTime = +new Date;

          sendInfo({ at: 1 });
          checkWindow();
      })
  })()

})(this)