阅读更多

2顶
0踩

Web前端
本文转自破狼的博客

浏览器事件模型和回调机制

JavaScript作为单线程运行于浏览器之中,这是每本JavaScript教科书中都会被提到的。同时出于对UI线程操作的安全性考虑,JavaScript和UI线程也处于同一个线程中。因此对于长时间的耗时操作,将会阻塞UI的响应。为了更好的UI体验,应该尽量的避免JavaScript中执行较长耗时的操作(如大量for循环的对象diff等)或者是长时间I/O阻塞的任务。所以在浏览器中的大多数任务都是异步(无阻塞)执行的,例如:鼠标点击事件、窗口大小拖拉事件、定时器触发事件、Ajax完成回调事件等。当每一个异步事件完成时,它都将被放入一个叫做”浏览器事件队列“中的事件池中去。而这些被放在事件池中的任务,将会被javascript引擎单线程处理的一个一个的处理,当在此次处理中再次遇见的异步任务,它们也会被放到事件池中去,等待下一次的tick被处理。另外在HTML5中引入了新的组件-Web Worker,它可以在JavaScript线程以外执行这些任务,而不阻塞当前UI线程。

浏览器中的事件循环模型如下图所示:



由于浏览器的这种内部事件循环机制,所以JavaScript一直以callback回调的方式来处理事件任务。因此无所避免的对于多个的JavaScript异步任务的处理,将会遇见”callback hell“(回调地狱),使得这类代码及其不可读和难易维护。
asyncTask1(data, function (data1){

    asyncTask2(data1, function (data2){

        asyncTask3(data2, function (data3){
                // .... 魔鬼式的金字塔还在继续
        });

    });

});

Promise的横空出世
因此很多JavaScript牛人开始寻找解决这回调地狱的模式设计,随后Promise(jQuery的deferred也属于Promise范畴)便被引入到了JavaScript的世界。Promise在英语中语义为:”承诺“,它表示如A调用一个长时间任务B的时候,B将返回一个”承诺“给A,A不用关心整个实施的过程,继续做自己的任务;当B实施完成的时候,会通过A,并将执行A之间的预先约定的回调。而deferred在英语中语义为:”延迟“,这也说明promise解决的问题是一种带有延迟的事件,这个事件会被延迟到未来某个合适点再执行。

Promise/A+规范:
  • Promise 对象有三种状态: Pending – Promise对象的初始状态,等到任务的完成或者被拒绝;Fulfilled – 任务执行完成并且成功的状态;Rejected – 任务执行完成并且失败的状态;
  • Promise的状态只可能从“Pending”状态转到“Fulfilled”状态或者“Rejected”状态,而且不能逆向转换,同时“Fulfilled”状态和“Rejected”状态也不能相互转换;
  • Promise对象必须实现then方法,then是promise规范的核心,而且then方法也必须返回一个Promise对象,同一个Promise对象可以注册多个then方法,并且回调的执行顺序跟它们的注册顺序一致;
  • then方法接受两个回调函数,它们分别为:成功时的回调和失败时的回调;并且它们分别在:Promise由“Pending”状态转换到“Fulfilled”状态时被调用和在Promise由“Pending”状态转换到“Rejected”状态时被调用。

如下面所示:



根据Promise/A+规范,我们在文章开始的Promise伪代码就可以转换为如下代码:
asyncTask1(data)
    .then(function(data1){
        return asyncTask2(data1);
    })
    .then(function(data2){
       return asyncTask3(data2);
    })
    // 仍然可以继续then方法

Promise将原来回调地狱中的回调函数,从横向式增加巧妙的变为了纵向增长。以链式的风格,纵向的书写,使得代码更加的可读和易于维护。

Promise在JavaScript的世界中逐渐的被大家所接受,所以在ES6的标准版中已经引入了Promise的规范了。现在通过Babel,可以完全放心的引入产品环境之中了。

另外,对于解决这类异步任务的方式,在ES7中将会引入async、await两个关键字,以同步的方式来书写异步的任务,它被誉为”JavaScript异步处理的终极方案“。这两个关键字是ES6标准中生成器(generator)和Promise的组合新语法,内置generator的执行器的一种方式。当然async、await的讲解并不会在本文中展开,有兴趣的读者可以参见MDN资料。

Promise的妙用

如上所说Promise在处理异步回调或者是延迟执行任务时候,是一个不错的选择方案。下面我们将介绍一些Promise的使用技巧(下面将利用Angular的$q和$http为例,当然对于jQuery的deferred,ES6的Promise仍然实用):

多个异步任务的串行处理

在上文中提到的回调地狱案例,就是一种试图去将多个异步的任务串行处理的结果,使得代码不断的横向延伸,可读性和维护性急剧下降。当然我们也提到了Promise利用链式和延迟执行模型,将代码从横向延伸拉回了纵向增长。使用Angular中$http的实现如下:
$http.get('/demo1')
 .then(function(data){
     console.log('demo1', data);
     return $http.get('/demo2', {params: data.result});
  })
 .then(function(data){
     console.log('demo2', data);
     return $http.get('/demo3', {params: data.result});
  })
 .then(function(data){
     console.log('demo3', data.result);
  });

因为Promise是可以传递的,可以继续then方法延续下去,也可以在纵向扩展的途中改变为其他Promise或者数据。所以在例子中的$http也可以换为其他的Promise(如$timeout,$resource …)。

多个异步任务的并行处理

在有些场景下,我们所要处理的多个异步任务之间并没有像上例中的那么强的依赖关系,只需要在这一系列的异步任务全部完成的时候执行一些特定逻辑。这个时候为了性能的考虑等,我们不需要将它们都串行起来执行,并行执行它们将是一个最优的选择。如果仍然采用回调函数,则这是一个非常恼人的问题。利用Promise则同样可以优雅的解决它:
$q.all([$http.get('/demo1'),
        $http.get('/demo2'),
        $http.get('/demo3')
])
.then(function(results){
    console.log('result 1', results[0]);
    console.log('result 2', results[1]);
    console.log('result 3', results[2]);
});

这样就可以等到一堆异步的任务完成后,在执行特定的业务回调了。在Angular中的路由机制ngRoute、uiRoute的resolve机制也是采用同样的原理:在路由执行的时候,会将获取模板的Promise、获取所有resolve数据的Promise都拼接在一起,同时并行的获取它们,然后等待它们都结束的时候,才开始初始化ng-view、ui-view指令的scope对象,以及compile模板节点,并插入页面DOM中,完成一次路由的跳转并且切换了View,将静态的HTML模板变为动态的网页展示出来。

Angular路由机制的伪代码如下:
var getTemplatePromise = function(options) {
         // ... 拼接所有template或者templateUrl
    };

    var getResolvePromises = function(resolves) {
        // ... 拼接所有resolve
    };

    var controllerLoader = function(options, currentScope, tplAndVars, initLocals) {
        // ...

        ctrlInstance = $controller(options.controller, ctrlLocals);
        if (options.controllerAs) {
            currentScope[options.controllerAs] = ctrlInstance;
        }

        // ...

        return currentScope;
    };

    var templateAndResolvePromise = $q.all([getTemplatePromise(options)].concat(getResolvePromises(options.resolve || {})));

    return templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
        var currentScope = currentScope || $rootScope.$new();
        controllerLoader(options, currentScope, tplAndVars, initLocals);
        // ... compile & append dom 
    });

对于这类路由机制的使用,在博主上篇博文《自定义Angular插件 – 网站用户引导》中的ng-trainning插件中也采用了它。关于这段代码的具体分析和应用将在后续单独的文章中,敬请大家期待。

对于同步数据的Promise处理,统一调用接口

有了Promise的处理,因为在前端代码中最多的异步处理就是Ajax,它们都被包装为了Promise .then的风格。那么对于一部分同步的非异步处理呢?如localStorage、setTimeout、setInterval之类的方法。在大多数情况下,博主仍然推荐使用Promise的方式包装,使得项目Service的返回接口统一。这样也便于像上例中的多个异步任务的串行、并行处理。在Angular路由中对于只设置template的情况,也是这么处理的。

对于setTimeout、setInterval在Angular中都已经为我们内置了$timeout和$interval服务,它们就是一种Promise的封装。对于localStorage呢?可以采用$q.when方法来直接包装localStorage的返回值的为Promise接口,如下所示:
var data = $window.localStorage.getItem('data-api-key');
    return $q.when(data);

整个项目的Service层的返回值都可以被封装为统一的风格使用了,项目变得更加的一致和统一。在需要多个Service同时并行或者串行处理的时候,也变得简单了,一致的使用方式。

对于延迟任务的Promise DSL语义化封装

在前面已经提到Promise是延迟到未来执行某些特定任务,在调用时候则给消费者返回一个”承诺“,消费者线程并不会被阻塞。在消费者接受到”承诺“之后,消费者就不用再关心这些任务是如何完成的,以及督促生产者的任务执行状态等。直到任务完成后,消费者手中的这个”承诺“就被兑现了。

对于这类延迟机制,在前端的UI交互中也是极其常见的。比如模态窗口的显示,对于用户在模态窗口中的交互结果并不可提前预知的,用户是点击”ok“按钮,或者是”cancel“按钮,这是一个未来将会发生的延迟事件。对于这类场景的处理,也是Promise所擅长的领域。在Angular-UI的Bootstrap的modal的实现也是基于Promise的封装。
$modal.open({
    templateUrl: '/templates/modal.html',
    controller: 'ModalController',
    controllerAs: 'modal',
    resolve: {
    }
})
    .result
    .then(function ok(data) {
        // 用户点击ok按钮事件
    }, function cancel(){
        // 用户点击cancel按钮事件
    });

这是因为modal在open方法的返回值中给了我们一个Promise的result对象(承诺)。等到用户在模态窗口中点击了ok按钮,则Bootstrap会使用$q的defer来resolve来执行ok事件;相反,如果用户点击了cancel按钮,则会使用$q的defer来reject执行cancel事件。

这样就很好的解决了延迟触发的问题,也避免了callback的地狱。我们仍然可以进一步将其返回值语义化,以业务自有的术语命名而形成一套DSL API。
function open(data){
    var defer = $q.defer();

    // resolve or reject defer;

    var promise = defer.promise;
    promise.ok = function(func){
        promise.then(func);
        return promise;
    };

    promise.cancel = function(func){
        promise.then(null, func);
        return promise;
    };

    return promise;
};

则我们可以如下方式来访问它:
$modal.open(item)
   .ok(function(data){
        // ok逻辑
   })
   .cancel(function(data){
       // cancel 逻辑
   });

是不是感觉更具有语义呢?在Angular中$http的返回方法success、error也是同样逻辑的封装。将success的注册函数注册为.then方法的成功回调,error的注册方法注册为then方法的失败回调。所以success和error方法只是Angular框架为我们在Promise语法之上封装的一套语法糖而已。

Angular的success、error回调的实现代码:
promise.success = function(fn) {
    promise.then(function(response) {
      fn(response.data, response.status, response.headers, config);
    });
    return promise;
  };

  promise.error = function(fn) {
    promise.then(null, function(response) {
      fn(response.data, response.status, response.headers, config);
    });
    return promise;
  };

利用Promise来实现管道式AOP拦截

在软件设计中,AOP是Aspect-Oriented Programming的缩写,意为:面向切面编程。通过编译时(Compile)植入代码、运行期(Runtime)动态代理、以及框架提供管道式执行等策略实现程序通用功能与业务模块的分离,统一处理、维护的一种解耦设计。 AOP是OOP的延续,是软件开发中的一个热点,也是很多服务端框架(如Java世界的Spring)中的核心内容之一,是函数式编程的一种衍生范型。 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高开发效率。 AOP实用的场景主要有:权限控制、日志模块、事务处理、性能统计、异常处理等独立、通用的非业务模块。关于更多的AOP资料请参考http://en.wikipedia.org/wiki/Aspect-oriented_programming

在Angular中同样也内置了一些AOP的设计思想,便于实现程序通用功能与业务模块的分离、解耦、统一处理和维护。$http中的拦截器(interceptors)和装饰器($provide.decorator)是Angular中两类常见的AOP切入点。前者以管道式执行策略实现,而后者则通过运行时的Promise管道动态实现的。

首先回顾一下Angular的拦截器实现方式:
// 注册一个拦截器服务
$provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
  return {
    // 可选方法
    'request': function(config) {
      // 请求成功后处理
      return config;
    },

    // 可选方法
   'requestError': function(rejection) {
      // 请求失败后的处理
      if (canRecover(rejection)) {
        return responseOrNewPromise
      }
      return $q.reject(rejection);
    },



    // 可选方法
    'response': function(response) {
      // 返回回城处理
      return response;
    },

    // 可选方法
   'responseError': function(rejection) {
      // 返回失败的处理
      if (canRecover(rejection)) {
        return responseOrNewPromise
      }
      return $q.reject(rejection);
    }
  };
});

// 将服务注册到拦截器链中
$httpProvider.interceptors.push('myHttpInterceptor');


// 同样也可以将拦截器注册为一个工厂方法。 但上一中方式更为推荐。
$httpProvider.interceptors.push(['$q', function($q) {
  return {
   'request': function(config) {
       // 同上
    },

    'response': function(response) {
       // 同上
    }
  };
}]);

这样就可以实现对Angular中的$http或者是$resource的Ajax请求拦截了。但在Angular内部是是如何实现这种拦截方式的呢?Angular使用的就是Promise机制,形成异步管道流,将真实的Ajax请求放置在request、requestError和response、responseError的管道中间,因此就产生了对Ajax请求的拦截。

其源码实现如下:
var interceptorFactories = this.interceptors = [];

var responseInterceptorFactories = this.responseInterceptors = [];

this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
  function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {

    var defaultCache = $cacheFactory('$http');

    var reversedInterceptors = [];

    forEach(interceptorFactories, function(interceptorFactory) {
      reversedInterceptors.unshift(isString(interceptorFactory) ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
    });

    forEach(responseInterceptorFactories, function(interceptorFactory, index) {
      var responseFn = isString(interceptorFactory) ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory);

      reversedInterceptors.splice(index, 0, {
        response: function(response) {
          return responseFn($q.when(response));
        },
        responseError: function(response) {
          return responseFn($q.reject(response));
        }
      });
    });
    ...

function $http(requestConfig) {
  ...

  var chain = [serverRequest, undefined];
  var promise = $q.when(config);

  // apply interceptors
  forEach(reversedInterceptors, function(interceptor) {
    if (interceptor.request || interceptor.requestError) {
      chain.unshift(interceptor.request, interceptor.requestError);
    }
    if (interceptor.response || interceptor.responseError) {
      chain.push(interceptor.response, interceptor.responseError);
    }
  });

  while (chain.length) {
    var thenFn = chain.shift();
    var rejectFn = chain.shift();

    promise = promise.then(thenFn, rejectFn);
  }

  promise.success = function(fn) {
    promise.then(function(response) {
      fn(response.data, response.status, response.headers, config);
    });
    return promise;
  };

  promise.error = function(fn) {
    promise.then(null, function(response) {
      fn(response.data, response.status, response.headers, config);
    });
    return promise;
  };

  return promise;
};

在上面紧接着在$get注入方法之后,Angular会将interceptors和responseInterceptors反转合并到一个reversedInterceptors的拦截器内部变量中保存。最后在$http函数中以[serverRequest, undefined](serverRequest是Ajax请求的Promise操作)为中心,将reversedInterceptors中的所有拦截器函数依次加入到chain链式数组中。如果是request或requestError,那么就放在链式数组起始位置;相反如果是response或responseError,那么就放在链式数组最后。

注意添加在chain的request和requestError或者response和responseError都一定是成对的,换句话说可能注册一个非空的request与一个为undefined的requestError,或者是一个为undefined的request与非空的requestError。就像chain数组的声明一样(var chain = [serverRequest, undefined];),成对的放入serverRequest和undefined对象到数组中。因为后面的代码将利用Promise的机制注册这些拦截器函数,实现管道式AOP拦截机制。

在Promise中需要两个函数来注册回调,分别为成功回调和失败回调。在这里request和response会被注册成Promise的成功回调,而requestError和responseError则会注册成Promise的失败回调。所以在chain中添加的request和requestError,response或responseError都是成对出现的,这是为了能在接下来的循环中简洁地注册Promise回调函数。 这些被注册的拦截器链,会通过$q.when(config) Promise启动,它会首先传入$http的config对象,并执行所有的request拦截器,依次再到serverRequest这个Ajax请求,此时将挂起后边所有的response拦截器,直到Ajax请求响应完成,再依次执行剩下的response拦截器回调; 如果在request过程中有异常失败则会执行后边的requestError拦截器,对于Ajax请求的失败或者处理Ajax的response拦截器的异常也会被后面注册的responseError拦截器捕获。

从最后两段代码也能了解到关于$http服务中的success方法和error方法,是Angular为大家提供了一种Promise的便捷写法。success方法是注册一个传入的成功回调和为undefined的错误回调,而error则是注册一个为null的成功回调和一个传入的失败回调。

总结

写到这里,本文也进入了尾声。希望大家能够对Promise有一定的理解,并能够”信手拈来“的运用于实际的项目之中,增强代码的可读性和可维护性。在本文中所用到的例子,你都可以在博主的jsbinhttp://jsbin.com/bayeva/edit?html,js,output中找到它们。

本文转自:破狼Blog
  • 大小: 38 KB
  • 大小: 20.8 KB
来自: 破狼Blog
2
0
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • [转]JavaScript/Node.JS 中的 Promises

    在JavaScript中把很多嵌套callback的方式变成promise方式,让代码更简单

  • 使用Promise

    Promise所要解决的问题:回调地狱 asyncTask1(data, function (data1){ asyncTask2(data1, function (data2){ asyncTask3(data2, function (data3){ // .... 魔鬼式的金字塔还在继续 }); }...

  • Promise进一步阅读

    [3] Promise的前世今生和妙用技巧, http://www.cnblogs.com/whitewolf/archive/2015/10/22/4902570.html [4] 谈谈使用promise时候的一些反模式,  http://www.wtoutiao.com/p/p63e4h.html ...

  • JavaScript单线程和浏览器事件循环简述

    在上篇博客《Promise的前世今生和妙用技巧》的开篇中,我们曾简述了JavaScript的单线程机制和浏览器的事件模型。应很多网友的回复,在这篇文章中将继续展开这一个话题。当然这里是博主的一些理解,如果还存在什么...

  • JavaScript单线程和浏览器事件循环概述

    在上篇博客《Promise的前世今生和妙用技巧》的开篇中,我们曾简述了JavaScript的单线程机制和浏览器的事件模型。应很多网友的回复,在这篇文章中将继续展开这一个话题。当然这里是博主的一些理解,如果还存在什么...

  • 华为OD机试D卷 - 用连续自然数之和来表达整数 - 免费看解析和代码.html

    私信博主免费获取真题解析以及代码

  • Screenshot_2024-05-10-20-21-01-857_com.chaoxing.mobile.jpg

    Screenshot_2024-05-10-20-21-01-857_com.chaoxing.mobile.jpg

  • 数字图像处理|Matlab-频域增强实验-彩色图像的频域滤波.zip

    数字图像处理|Matlab-频域增强实验-彩色图像的频域滤波.zip

  • 2024-2030中国定向转向膜市场现状研究分析与发展前景预测报告.docx

    2024-2030中国定向转向膜市场现状研究分析与发展前景预测报告

  • 开源工时填报管理系统安装包

    开源工时填报管理系统安装包

  • 激光雷达深度报告:产业化加速,国产供应链迎来投资机遇.pdf

    电子元件 电子行业 行业分析 数据分析 数据报告 行业报告

  • node-v0.12.10-darwin-x86.tar.gz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

  • 18-17.网站域名DNS被劫持,网站服务器密码被改.mp4

    18-17.网站域名DNS被劫持,网站服务器密码被改.mp4

  • QYResearch:2023年前五大2,3,3',4'-联苯四甲酸二酐(α-BPDA)企业占据全球91%的市场份额.docx

    QYResearch:2023年前五大2,3,3',4'-联苯四甲酸二酐(α-BPDA)企业占据全球91%的市场份额.docx

  • 2024-2030中国仿生智能餐饮机器人市场现状研究分析与发展前景预测报告.docx

    2024-2030中国仿生智能餐饮机器人市场现状研究分析与发展前景预测报告

  • 82-82.渗透测试-CVE-2017-8464“震网三代 反弹shell演示课件.mp4

    82-82.渗透测试-CVE-2017-8464“震网三代 反弹shell演示课件.mp4

  • node-v6.11.5-darwin-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

  • 33-33.渗透测试渗透测试之SQL注入基于报错注入(下)

    渗透测试渗透测试之SQL注入基于报错注入(下)

  • 基于Android的云游四海手机端应用.zip

    Android是一种基于Linux内核(不包含GNU组件)的自由及开放源代码的移动操作系统,主要应用于移动设备,如智能手机和平板电脑。该系统最初由安迪·鲁宾开发,后被Google公司收购并注资,随后与多家硬件制造商、软件开发商及电信营运商共同研发改良。 Android操作系统的特点包括: 开放源代码:Android系统采用开放源代码模式,允许开发者自由访问、修改和定制操作系统,这促进了技术的创新和发展,使得Android系统具有高度的灵活性和可定制性。 多任务处理:Android允许用户同时运行多个应用程序,并且可以轻松地在不同应用程序之间切换,提高了效率和便利性。 丰富的应用生态系统:Android系统拥有庞大的应用程序生态系统,用户可以从Google Play商店或其他第三方应用市场下载和安装各种各样的应用程序,满足各种需求。 可定制性:Android操作系统可以根据用户的个人喜好进行定制,用户可以更改主题、小部件和图标等,以使其界面更符合个人风格和偏好。 多种设备支持:Android操作系统可以运行在多种不同类型的设备上,包括手机、平板电脑、智能电视、汽车导航系统等。 此外,Android系统还有一些常见的问题,如应用崩溃、电池耗电过快、Wi-Fi连接问题、存储空间不足、更新问题等。针对这些问题,用户可以尝试一些基本的解决方法,如清除应用缓存和数据、降低屏幕亮度、关闭没有使用的连接和传感器、限制后台运行的应用、删除不需要的文件和应用等。 随着Android系统的不断发展,其功能和性能也在不断提升。例如,最新的Android版本引入了更多的安全性和隐私保护功能,以及更流畅的用户界面和更强大的性能。此外,Android系统也在不断探索新的应用场景,如智能家居、虚拟现实、人工智能等领域。 总之,Android系统是一种功能强大、灵活可定制、拥有丰富应用生态系统的移动操作系统,在全球范围内拥有广泛的用户基础。

Global site tag (gtag.js) - Google Analytics