http://www.ox-holdings.com

GPU 频率等来提升 新匍京a奥门:APP 性能,通过系统调用

摘要微信自用的安卓APP与系统间通信解决方案——Hardcoder已开源,该方案能让微信的整体性能提升10%-30%。1、Hardcoder 的诞生随着微信越来越复杂,性能优化变得越来越难做,优化所带来的效果提升也越来越不明显。所以我们⼀直在思考,该如何突破这个优化的极限?直到有一次与厂商的交流我们了解到,部分厂商会针对微信做一些小改动,其中比较典型的就是“暴力提频”。系统在识别到微信启动,页面切换等场景时,会粗暴地提高 CPU 频率,从而提升 APP 运行的性能。但由于厂商无法准确判断微信场景,暴力提频效果并不理想;而如果过多地提高 CPU 频率,又对手机的功耗有影响。这一方案启发了我们,我们何不跳出软件的范畴,在手机硬件的层面上挖掘更多的性能优化空间呢?于是 Hardcoder 框架应运而生。2、Hardcoder 是什么厂商暴力提频效果不理想是由于在目前 Android 框架下,手机没有办法准确获知 APP 需要资源的时机。如果我们需要挖掘手机硬件层面的性能优化,就需要跳过 Android 操作系统的应用框架,在应用开发者和硬件之间打开一个通道,让硬件可以直接根据应用开发者的需要进行资源的调度。Hardcoder 构建了 APP 与系统(ROM)之间可靠的通信框架,突破了 APP 只能调用系统标准 API,无法直接调用系统底层硬件资源的问题,让 Android APP 和系统能实时通信。利用 Hardcoder,APP 能充分调度系统资源如 CPU 频率,大小核,GPU 频率等来提升 APP 性能,系统能够从 APP 侧获取更多信息以便更合理提供各项系统资源。同时,对于 Android 缺乏标准接口实现的功能,APP 和系统间也可以通过该框架实现机型适配和功能拓展。3、Hardcoder 框架通信流程Hardcoder 框架分为 Server 端和 Client 端。其中 Server 端在厂商系统侧实现,Client 端以 aar 形式合入到 APP中。APP 在需要资源的时候,向 Hardcoder 的 Client 端发出请求。Hardcoder Client 端接收到请求后向 Hardcoder Server 端发出请求。Server 端接受到请求后会根据请求参数向硬件申请不同的资源,比如调整 CPU 频率,把线程绑定到大核运行等,实现了 APP 到系统的通信。同时系统也可把当前系统的状态通过 Hardcoder Client 在 Server 端注册的接口回调通知到 Client 端,从而 APP 可以获取到系统状态,实现系统到 APP 的通信。Hardcoder Client 端与 Server 端采用的是 LocalSocket 的通信方式,由于 Hardcoder 采用 Native 实现,因而在 C 层使用 Linux 的 socket 接口实现了一套 LocalSocket 机制作为 Client 端与 Server 端之间的通信方式。Hardcoder 通信框架有以下特点:1)系统服务为 optional,实现上可以完全支持或者部分支持;2)框架实现不依赖于特定 Android 系统,如 API level 限制;3)APP 的功能和业务特性不依赖于该框架。4、Hardcoder 适用场景和效果Hardcoder 框架有效提升了微信启动、发送视频、小程序启动等重度场景的速度,朋友圈的滑动流畅性也明显提升,平均优化效果达 10%-30%。此外,由于微信作为主动请求方可以在场景资源把控上做得更精细和准确,Hardcoder 在性能得到提升的同时仅增加了 2% 的电量消耗,相当于用 2% 的功耗换取平均 20% 的性能提升。Hardcoder 框架目前已接入 OPPO、vivo、华为、小米、三星、魅族等主流手机厂商,覆盖 4.6 亿+ 设备量。5、Hardcoder 开源从微信技术开放共享的理念出发,我们在腾讯内部进行了 Hardcoder 框架的宣传和推广,包括手机 QQ、企业微信、天天快报等多个应用团队接入。其中手机 QQ 接入 Hardcoder 后,在启动、打开聊天界面、发送图片等场景的平均优化效果达 10%-50%。我们现将 Hardcoder 框架开源,让更多 Android 开发者享受到 Hardcoder 框架的价值,解决大家在性能优化和机型适配上的烦恼。欢迎大家查阅 github 网址: Hardcoder一、通过 Hardcoder 技术方案介绍,了解 Hardcoder 实现原理以及框架;二、使用工程自带 testapp 快速使用 Hardcoder 并验证效果,具体请见 Hardcoder Testapp 测试指南;三、APP 接入 Hardcoder,具体请参见 Hardcoder 接入指南:1)下载 Hardcoder 工程编译 aar;2)项目 build.gradle 引入 Hardcoder aar;3)进程启动时调用 initHardCoder 建立 socket 连接(一般进程启动时需要请求资源,因而推荐在进程启动时调用)。每个进程都是独立的,都需要调用 initHardCoder 建立 socket 连接,建立连接后每个进程维持一个 socket,进程退出时 socket 也会断开;4)initHardCoder 回调成功后调用 checkPermission,传入 APP 已申请的各个厂商鉴权值;5)在需要请求资源的场景调用 startPerformance,传入请求资源的参数。若场景位于进程启动阶段,比如 APP 启动,需要在 initHardCoder 的回调成功以后再调用 startPerformance,确保连接已成功建立,或者判断 HardCoderJNI 的 isConnect() 检查 socket 是否已连接。6)场景结束时主动调用 stopPerformance,传入对应场景 startPerformance 时的返回值 hashCode 作为参数,停止本次请求。7)测试性能,APP 可对打开/关闭 Hardcoder 的情况做对比实验,测试性能是否有提升。四、向厂商申请线上权限,具体请见常见问题;五、发布带 Hardcoder 功能的 APP。附录: github的wiki 文档链接Hardcoder产品方案介绍: 技术方案介绍: testapp 测试指南: 接入指南:

LocalSocket,在Unix域名空间创建的一个套接字(非服务端)。

(Android系统所基于的)Linux内核基础知识(为什么需要跨进程)

  1. 进程隔离/虚拟地址空间:每个进程对应一个虚拟地址空间,不同进程数据不共享。
  2. 系统调用:Linux分内核空间和用户空间,应用程序存在于用户空间,不能直接访问内核空间,但可以通过系统调用间接访问。这样间接的方式可对访问权限进行限制,保证系统的安全。跨进程通信的原理,就是通过系统调用功能,使用内核空间作为桥梁,完成不同应用程序间的通信。

三、详解IPC

  五个方面阐述android 使用binder机制原因。

ClientFinallyActivity.java

Binder的总结

  1. 通常意义下,Binder指的是一种通信机制。(以上的Binder是Binder对象的简称)
  2. 对于Server进程来说,Binder对象指的是Binder本地对象,对于Client来说,Binder对象指的是Binder代理对象。
  3. 对于传输过程而言,Binder对象是可以进行跨进程传递的对象。

新匍京a奥门 1

  1.数据拷贝:binder机制只要一次copy,其他机制需要2次copy,只有共享内存不需要copy内存。

在这里通过LocalSocket实现应用之间的通信,可以接收、发送数据,同时将接收到的数据通过activty显示。

扩展

匿名Binder:没有向ServiceManager注册的Binder对象,即该Binder对象的引用没有公开,能收到此匿名Binder引用的Client,便可以和Server建立起一条私密的通道。(具体Client怎么才能收到,在文章中没找到)

IPC,即Inner-Process-communication,进程间通信,是Android系统中比较难以理解的一个概念。而Binder则是Android系统中特有的进行IPC的一种方式,相对于Linux自带的其他方式(如管道)、socket、文件等而言,Binder具有更大的效率和安全优势。而本文将从各个层面深入探究Binder的原理。

  4.语言层面:linux是基于C语言的,而andriod是基于面向对象的。binder恰恰是面向对象的,将进程间通信,转化为对另一个进程对象的引用。其独特之处,就是binder对象作为一个可以跨进程引用的对象,它的实体位于一个进程中,而它的引用遍布于各个进程中,大家使用同一个进度对象,就像面向对象的类一样,可以供各个系统使用。binder弱化了进程间通信概念,使得调用就像同一个进程一样。这就把进程间的通信演化为面向对象类的调用,所以binder是为android量身定制的。

Server端:

Binder通信模型

  1. Binder驱动:“驱动”是保证计算机能操作硬件设备的一小块代码,与此类似,“Binder驱动”是Android系统中保证Binder对象能操作内核空间的一小块代码。
  2. Binder(对象,或实体):提供了间接访问内核空间的功能,由Server创建并向ServiceManager注册,Client可以通过对Binder对象的引用与Server进行通信。所谓注册,就是将对象的名字和引用的映射关系记录在ServiceManager中。
  3. Binder引用(或代理对象):Client对Binder对象所持有的引用,或称代理对象,由Binder驱动根据Binder对象创建。Client通过它可以调用Binder对象的功能,实现方式由Binder驱动完成。
  4. Client和Server(进程):假设发起和接收通信请求的进程分别为Client和Server。
  5. ServiceManager(进程):作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得Binder引用。(ServiceManager也是一个进程,对于ServiceManager而言,其他所有的进程都是Client,也就是说,它是其他所有进程的Server。它的Binder引用是0号引用,该Binder没有名字也不需要注册。其它进程要通过0号引用向ServiceManager发起注册Binder或获取Binder引用的请求)
  6. Client、Server、ServiceManager运行于用户空间,Binder驱动运行于内核空间。
  7. 通信流程:
    1. ServiceManager的建立:进程向Binder驱动申请为ServiceManager,此时进程还没有区分为Client角色或Server角色,所以统称之为Service。(我理解的建立是,其他进程通过0号引用和ServiceManager建立了连接,不知道是不是这样)
    2. Server创建Binder对象并向ServiceManager注册。
    3. Client通过Binder的名字向ServiceManager查询和获取Binder引用。
    4. Client通过Binder引用与Server通信,Binder通过操作内核空间,实现了两者的跨进程通信。

新匍京a奥门 2

Binder通信示意图


  BpBinder:  BpBinder,transact,发送事物信息。

package com.fqhx.remoteserviceclient;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

public class ClientFinallyActivity extends Activity {

    ClientFinally client;
    EditText et_clientSend;
    TextView tv_showReceiveDataClient;

//    ClientLastly client;
    StringBuffer receiveData=new StringBuffer();

    Handler handler=new Handler(new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            if (msg.arg1==0x12) {
                receiveData.append((String)msg.obj);
                tv_showReceiveDataClient.setText(receiveData);
                receiveData.append("rn");
            }

            return false;
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client_finally);

        et_clientSend=(EditText) findViewById(R.id.et_clientSend);
        tv_showReceiveDataClient=(TextView) findViewById(R.id.tv_showReceiveDataClient);

        client=new ClientFinally(handler);
        new Thread(client).start();

//        client=new ClientLastly(handler);
//        new Thread(client).start();
    }


    public void btn_clientSend(View view){
        client.send(et_clientSend.getText().toString()+"");
        et_clientSend.setText("");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        client.close();
    }
}

其中的各种概念和比喻让我看的十分痛苦,不知道是我的理解能力太差,还是人类的语言太苍白。我按自己的理解整理出一下笔记。主要是对概念的梳理,刨除比喻,以简要的文字,把我从这三篇文章中理解到的内容阐述出来,理解深度也根据自己的水平来。当时内心急躁,没有细看,也没有全看。有人看到我这篇文章了,如发现问题,欢迎指正。

二、何为Binder
1.广义来讲,Binder可以理解为Android的一种跨进程通信方式。
2.从Android源码来看,Binder是一个Java类,实现了IBinder接口。
3.从Android的FrameWork来看,Binder可以看做是ServiceManager连接各种系统自带的Manager(如ActivityManager、WindowManager)和对应的Service(ActivityManagerService,WindowManagerService)的桥梁。
4.从应用层面来讲,Binder是客户端和服务端通信的媒介。当客户端对服务端进行绑定时,服务端会返回一个包含了服务端业务调用的Binder类对象,客户端可以通过此对象调用服务端的方法和数据。

  C/S模式:

package com.fqhx.remoteserviceclient;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
/**
 * 通过LocalSocket实现
 * @author Administrator
 *
 */
public class ClientFinally implements Runnable{
    private static final String TAG="ClientFinally";
    private static final String NAME="com.repackaging.localsocket";
    private int timeout=30000;
    LocalSocket client;
    PrintWriter os;
    BufferedReader is;

    Handler handler;

    ClientFinally(Handler handler){
        this.handler=handler;
//        client=new LocalSocket();
//        try {
//            client.connect(new LocalSocketAddress(NAME));//连接服务器
//            Log.i(TAG, "Client=======连接服务器成功=========");
//            client.setSoTimeout(timeout);
//            os=new PrintWriter(client.getOutputStream());
//            is=new BufferedReader(new InputStreamReader(client.getInputStream()));
//        } catch (IOException e) {
//            // TODO Auto-generated catch block
//            e.printStackTrace();
//        }
    }

    //发数据
    public void send(String data){
        if (os!=null) {
            os.println(data);
            os.flush();
        }
    }

    @Override
    public void run() {
        client=new LocalSocket();
        try {
            client.connect(new LocalSocketAddress(NAME));//连接服务器
            Log.i(TAG, "Client=======连接服务器成功=========");
            client.setSoTimeout(timeout);
            os=new PrintWriter(client.getOutputStream());
            is=new BufferedReader(new InputStreamReader(client.getInputStream()));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        String result="";
        while(true){
            try {
                result=is.readLine();
                Log.i(TAG, "客户端接到的数据为:"+result);
                //将数据带回acitvity显示
                Message msg=handler.obtainMessage();
                msg.arg1=0x12;
                msg.obj=result;
                handler.sendMessage(msg);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

    public void close(){
        try {
            if (os!=null) {
                os.close();
            }
            if (is!=null) {
                is.close();
            }
            if(client!=null){
            client.close();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

参考
Android Bander设计与实现 - 设计篇
Binder学习指南
github_Binder机制

读到此处,相信大家还是很难对Binder的应用场景,实现方式和作用有很直观的理解,以下笔者将通过client调用Service的详细过程对其进行解析。

  一般设计可以建立一个伪设备来作为应用与内核之间进行数据交换的渠道。最常用的做法是使用伪字符设备,然后通过icotl,mmap,open,read,write,close这些标准的系统调用和这个设备交互。

 

为什么使用Binder

  1. Android广泛使用跨进程通信
  2. 性能:Binder拷贝数据的次数少,性能比socket、管道、消息队列等方式更高效
  3. 安全:Binder支持对通信双方进行身份校验

一、Linux基础
本文首先 介绍部分和Android的进程间通信有关的Linux基础知识。
1.进程隔离/虚拟地址空间:Linux系统为了避免进程之间互相干扰,给各个进程分配了一块虚拟的地址空间,各个进程分别运行在各自的虚拟地址空间内。
2.系统调用:Linux提供的一种调用机制,通过系统调用,用户控件可以访问内核的应用程序。

  2.netlink,使用socket API就可以进行。

package com.example.driverexam;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

public class ServerFinallyActivity extends Activity {

    EditText et_serverSend;
    TextView tv_showReceiveData;

    ServerFinally server;

//    ServerLastly server;
    StringBuffer receiveData=new StringBuffer();

    Handler handler=new Handler(new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            if (msg.arg1==0x11) {
                receiveData.append((String)msg.obj);
                tv_showReceiveData.setText(receiveData);
                receiveData.append("rn");
            }

            return false;
        }
    });


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        setContentView(R.layout.activity_server_finally);
        et_serverSend=(EditText) findViewById(R.id.et_serverSend);
        tv_showReceiveData=(TextView) findViewById(R.id.tv_showReceiveData);

        server=new ServerFinally(handler);
        new Thread(server).start();

//        server=new ServerLastly(handler);
//        new Thread(server).start();

    }

    //发数据
    public void btn_serverSend(View view){
        server.send(et_serverSend.getText().toString()+"");
        et_serverSend.setText("");
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        server.close();
    }

}

1、我们将主要围绕上图进行分析。
1)图中有四个关键的对象:Server、Client、SeviceManager(图中最下方的矩形)、Binder驱动。其中前三者运行在客户空间,而Binder驱动运行在内核空间。Server、Client由应用程序负责提供,而Binder驱动和SeviceManager由系统提供。
2)Binder进程间通信实在OpenBinder的基础上实现的,其中提供服务的进程分为服务进程,而访问服务的进程分为Client进程,两者分别运行在不同的进程中。而Server进程和Client进程需要通信,需要借助运行在内核空间的Binder驱动,Binder驱动对用户空间暴露了一个设备文件、dev/binder,应用程序可以依靠他来建立通信通道。
3)当Server空间中的Service启动时(不一定是Service子类,但需要继承IInterface接口),会在ServiceManager(下称SM)中去进行注册。SM可以看做是Binder进程通信间的上下文管理者,他也需要与Server和Client通信,可以看做是特殊的Service组件。
4)当Client需要调用Service(再次强调,这里指的Service指的是继承了IInterface的一个实现类,通常还有一个中间接口IService)中的方法或者数据时,需要根据类名在SM中进行查找,SM会返回服务端的代理对象,代理对象(Proxy)实现了Service所对应的接口(假设命名为IService,继承关系为:Service>IService>IInterface)的子类Stub,并对IService中定义的方法做了封装,使其可以实现进程间读写数据(需要需要序列化),因此代理对象可以将数据交由IService的最终实现类进行处理,完成之后再返回给Client。
上面就是Binder进程间通信机制的大致流程。下面将在分别对SM,Service组件启动和其代理对象具体获取过程作详细介绍。
5)SM:SM由系统负责启动,启动过程分为三个步骤:打开设备文件dev/binder将其映射到本进程的地址空间;将自己注册为Binder进程间通信的额上下文管理者;调用函数binder_loop来循环等待和处理Client的通信请求。SM中含有四个方法getService、checkService、addService、listService,Client通过getService获取Service的代理对象,Service通过addService将自己注册进SM中。SM运行在自己的独立空间,也继承了IInterface,所以它自身和Server及Client通信也需要跨进程:当Client需要通过SM的getService方法获取Service代理对象或者Service需要通过addService进行注册时时,也需要先获取SM的代理对象,但是不同的是其代理对象获取比较简单,Android系统在Binder库中提供了一个函数defaultServiceManager,通过此方法即可获取到。
6)Service启动过程:Service组件是在Server进程中运行的,Server进程启动时,首先将它里面的Service组件注册到SM中,接着再启动一个Binder线程池来等待和处理Client进程的通信请求。
7)Service代理对象获取:参考下面AIDL的解析。

 

是对Linux中Socket进行了封装,采用JNI方式调用,实现进程间通信。

20170711210214423.png

          

ClientFinally.java

四、IPC具体实现:AIDL

目前操作系统都使用虚拟存储技术,管理内存。

郑重声明:本文版权归新匍京a奥门-最全网站手机版app官方下载所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。