QSTitle创建流程

本文将分析Android6.0中下拉状态栏快捷开关QSTitle组件的创建流程,从开机init过程开始至具体的每个QSTitle对象具体的创建,如何添加入下拉状态栏,对QSTitle的相关整体流程进行梳理。

android设备上电,引导程序引导进入boot(通常是uboot),加载initramfs、kernel镜像,启动kernel后,进入用户态程序。第一个用户空间程序是init, PID固定是1.
在android系统上,init.cpp的代码位于/system/core/init下,基本功能有:

  • 管理设备
  • 解析并处理Android启动脚本init.rc
  • 实时维护这个init.rc中的服务,包括加载 Zygote

这里不过过多分析init.cpp部分,重点关注Zygote的启动流程。init,cpp部分推荐相关文档http://my.oschina.net/youranhongcha/blog/469028

init.rc配置文件在 Android 6.0 代码中位于 system/core/rootdir
/init.zygote32.rc,具体内容为:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd
    writepid /dev/cpuset/foreground/tasks

其中:

  1. 关键字 service 表示告诉 init 进程创建名为 zygote 的进程,所要执行的程序是 /system/bin/app_process,后面的都是传递的参数。
  2. 注意参数 --start-system-server,说明要启动 SystemServer
  3. socket zygote stream 660 root system 表示创建名为 zygote 的 socket。
  4. 后面的 onrestart 关键字表示 zygote 进程重启时所需执行的命令。

Zygote 的启动

从中我们可以得出结论: zygote 只是服务的名称,与此服务对应的程序是 app_process 程序,我们研究 zygote 的实现,就是要研究 app_process 程序。
app_process 代码位于 frameworks/base/cmds/app_process
/app_main.cpp,入口函数为 main。
main 的大体流程为:

  • 创建一个 AppRuntime 实例 runtime
  • 解析传入的命令行参数
  • 判断调用哪一个 runtime.start,传入对应的参数
  • 由于 init.zygote32.rc 中传入参数 -Xzygote /system/bin --zygote --start-system-server,因此将执行:
    runtime.start("com.android.internal.os.ZygoteInit", args, zygote);

由此可知,app_process 没干什么主要的事情,只是跳转到 Java 类 com.android.internal.os.ZygoteInit,看来工作都在 ZygoteInit 中完成。但ZygoteInit如何启动呢?
我们来看看AppRuntime 。

AndroidRuntime

runtime.start方法来自于 AppRuntime 的父类 AndroidRuntime,代码位于 frameworks/base/core/jni/AndroidRuntime.cpp。重点看看 AndroidRuntime start 方法,它主要做了三件事:

首先是创建虚拟机:

/* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

之后是调用 startReg 函数注册 JNI 方法:

/*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

最后构建参数、创建 JNI 类对象,获取 main 方法,并最终执行下面一行执行 main 入口:

  env->CallStaticVoidMethod(startClass, startMeth, strArray);

这样我们就成功从 AndroidRuntime.cpp中启动了一个虚拟机,并加载了 Java 类 ZygoteInit,并进入到它的 main 方法中执行。

ZygoteInit

之后就来到了 ZygoteInit.java 的 main 方法,代码位frameworks/
base/core/java/com/android/internal/os/ZygoteInit.java。来看其 main 方法:

public static void main(String argv[]) {
   try {
       ...
       registerZygoteSocket(socketName);
       ...
       if (startSystemServer) {
           startSystemServer(abiList, socketName);
       }
       ...
       runSelectLoop(abiList);
       ...
   } catch (...) { ... }
}

首先,调用 registerZygoteSocket 创建了一个名为 zygote 的 socket:

registerZygoteSocket(socketName);

之后启动 SystemServer 组件:

if (startSystemServer) {
startSystemServer(abiList, socketName);
}

最后调用 runSelectLoopMode 进入一个死循环,等待接受 socket 上由 ActivityManagerService 发来的请求创建应用程序的请求:

runSelectLoop(abiList);

对后startSystemServer(abiList, socketName);进行分析:
启动 SystemServer 组件
SystemServer 名为系统服务进程,负责启动 Android 系统的关键服务。来看看函数的主要实现:

private static boolean startSystemServer(String abiList, String socketName)
       throws MethodAndArgsCaller, RuntimeException {
    ...
    /* Request to fork the system server process */
    pid = Zygote.forkSystemServer(
        parsedArgs.uid, parsedArgs.gid,
        parsedArgs.gids,
        parsedArgs.debugFlags,
        null,
        parsedArgs.permittedCapabilities,
        parsedArgs.effectiveCapabilities);
    ...
    /* For child process */
    if (pid == 0) {
        ...
        handleSystemServerProcess(parsedArgs);
    }
    return true;
}

这里调用了 Zygote 的静态方法 forkSystemServer 来创建 SystemServer 进程。
在forkSystemServer 中进行JNI调用forkSystemServer - com_android_internal_os_Zygote_nativeForkSystemServer - ForkAndSpecializeCommon来成功fork一个新进程。
子进程创建好之后,有一句

handleSystemServerProcess(parsedArgs);

进入handleSystemServerProcess:

/**
     * Finish remaining work for the newly forked system server process.
     */
    private static void handleSystemServerProcess(
            ZygoteConnection.Arguments parsedArgs)
            throws ZygoteInit.MethodAndArgsCaller {
    ...
    final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
    ...
    if (...) {
        ...
    } else {
      ClassLoader cl = null;
      if (systemServerClasspath != null) {
          cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
          Thread.currentThread().setContextClassLoader(cl);
      }

      /*
       * Pass the remaining arguments to SystemServer.
       */
      RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
    }

从中可以看出,我们从环境变量 SYSTEMSERVERCLASSPATH 拿到 SystemServer 的类名,之后载入进来,最后使用 RuntimeInit.zygoteInit 来运行,它来执行 SystemServer 的 main 方法。

看一下RuntimeInit.zygoteInit():

public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
        throws ZygoteInit.MethodAndArgsCaller {
    ...
    commonInit();   // 基本设置(异常捕获、时区、HTTP User-Agent 等)
    nativeZygoteInit();
    applicationInit(targetSdkVersion, argv, classLoader); // 调用 Main 方法
}

其中 nativeZygoteInit() 是一个 jni 调用,位于 AndroidRuntime.cpp:

static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
    gCurRuntime->onZygoteInit();
}

onZygoteInit() 是 AppRuntime 中的方法,具体为:

virtual void onZygoteInit()
{
   sp<ProcessState> proc = ProcessState::self();
   ALOGV("App process: starting thread pool.\n");
   proc->startThreadPool();
}

启动了一个线程池。nativeZygoteInit() 就分析到这里,由此可见,Zygote 启动 SystemServer 的过程就算完了,之后的,都是 SystemServer 内部的事情了。

SystemServer.main()

上面说到RuntimeInit.zygoteInit 来执行 SystemServer.main方法。我们来看SystemServer.main():

/**
     * The main entry point from zygote.
     */
    public static void main(String[] args) {
        new SystemServer().run();
    }

生成SystemServer对象并跳转到了run();
去run()看看:

private void run() {
    ......
    
     // Start services.
        try {
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
            ......
       } catch (Throwable ex) {
           ...
            throw ex;
        }
        ...
        // Loop forever.
        Looper.loop();
        throw new RuntimeException("Main thread loop unexpectedly exited");
}

在这里启动了一些服务,我们重点去看startBootstrapServices();

  private void startBootstrapServices() {
        ......
        Installer installer = mSystemServiceManager.startService(Installer.class);

        // Activity manager runs the show.
        mActivityManagerService = mSystemServiceManager.startService(
                ActivityManagerService.Lifecycle.class).getService();
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        ......
 }

Activity manager运行
再回到SystemServer,run()有这么一句:

startOtherServices();

startOtherServices()代码:

private void startOtherServices() {
        final Context context = mSystemContext;
        AccountManagerService accountManager = null;
        ContentService contentService = null;
        .......
        
         mActivityManagerService.systemReady(new Runnable() {
            @Override
            public void run() {
              ......

                try {
                    startSystemUi(context);
                } catch (Throwable e) {
                    reportWtf("starting System UI", e);
                }
         .......

在这里mActivityManagerService.systemReady创建线程去调用startSystemUi(context);

static final void startSystemUi(Context context) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.android.systemui",
                    "com.android.systemui.SystemUIService"));
        //Slog.d(TAG, "Starting service: " + intent);
        context.startServiceAsUser(intent, UserHandle.OWNER);
    }

通过intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
设置启动systemui程序的SystemUIService
进入SystemUIService:

public class SystemUIService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();
    }
......

onCreate方法中获得SystemUIApplication对象并调用其startServicesIfNeeded方法:

 public void startServicesIfNeeded() {
        final int N = SERVICES.length;
        for (int i=0; i<N; i++) {
            Class<?> cl = SERVICES[i];
            try {
                mServices[i] = (SystemUI)cl.newInstance();//加载实例
            } catch (IllegalAccessException ex) {
                throw new RuntimeException(ex);
            } catch (InstantiationException ex) {
                throw new RuntimeException(ex);
            }
            mServices[i].mContext = this;
            mServices[i].mComponents = mComponents;
            mServices[i].start();//start服务
            if (mBootCompleted) {
                mServices[i].onBootCompleted();
            }
        }
        mServicesStarted = true;
    }

这个方法中,首先判断mServicesStarted标志为来判断SystemUI相关的服务是否启动,
同时根据系统配置文件来检查ActivityManagerService是否finishBooting。
可以看到这里有个mServices数组,并通过for循环加载它们的实例并调用它们的start();
但是mServices数组具体开启了哪些服务呢?
来看看SystemUIApplication类中的变量:

private final Class<?>[] SERVICES = new Class[] {
            com.android.systemui.tuner.TunerService.class,
            com.android.systemui.keyguard.KeyguardViewMediator.class,
            com.android.systemui.recents.Recents.class,
            com.android.systemui.volume.VolumeUI.class,
            com.android.systemui.statusbar.SystemBars.class,
            com.android.systemui.usb.StorageNotification.class,
            com.android.systemui.power.PowerUI.class,
            com.android.systemui.media.RingtonePlayer.class,
            com.android.systemui.keyboard.KeyboardUI.class,
    };

可以看到这里有很多sysytemui中常用的服务,后面将重点分析SystemBars及TunerService。
直接进入SystemBars.start():

public void start() {
        mServiceMonitor = new ServiceMonitor(TAG, DEBUG,
                mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);
        mServiceMonitor.start();  // will call onNoService if no remote service is found
    }

start中创建ServiceMonitor实例并start();
注释中说明,/服务没启动时,ServiceMonitor会回调SystemBars的onNoService/
所以去看SystemBars的onNoService:

public void onNoService() {
        createStatusBarFromConfig();  // fallback to using an in-process implementation
    }

直接去看createStatusBarFromConfig():

private void createStatusBarFromConfig() {
        final String clsName = mContext.getString(R.string.config_statusBarComponent);
        if (clsName == null || clsName.length() == 0) {
            throw andLog("No status bar component configured", null);
        }
        Class<?> cls = null;
        try {
            cls = mContext.getClassLoader().loadClass(clsName);
        } catch (Throwable t) {
            throw andLog("Error loading status bar component: " + clsName, t);
        }
        try {
            mStatusBar = (BaseStatusBar) cls.newInstance();
        } catch (Throwable t) {
            throw andLog("Error creating status bar component: " + clsName, t);
        }
        mStatusBar.mContext = mContext;
        mStatusBar.mComponents = mComponents;
        mStatusBar.start();
    }

clsName得到的string为com.android.systemui.statusbar.phone.PhoneStatusBar
通过反射机制得到PhoneStatusBar实例:

cls = mContext.getClassLoader().loadClass(clsName);
...
mStatusBar = (BaseStatusBar) cls.newInstance();

并调用start方法:
PhoneStatusBar继承自BaseStatusBar;
PhoneStatusBar中调用了BaseStatusBar的start()
BaseStatusBar.start():

public void start() {
        mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
        mDisplay = mWindowManager.getDefaultDisplay();
        mDevicePolicyManager = (DevicePolicyManager)mContext.getSystemService(
                Context.DEVICE_POLICY_SERVICE);

        mNotificationColorUtil = NotificationColorUtil.getInstance(mContext);

        mNotificationData = new NotificationData(this);

        mAccessibilityManager = (AccessibilityManager)
                mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);

        mDreamManager = IDreamManager.Stub.asInterface(
                ServiceManager.checkService(DreamService.DREAM_SERVICE));
        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);

        .......
        //在这里实例化了许多systemui常用的对象,服务,Manager,Observer等等
        .......
        
        createAndAddWindows(); //本本文而言一个非常重要的方法
        

查看createAndAddWindows():

 protected abstract void createAndAddWindows();

是个抽象方法,很显然调用去了BaseStatusBar的子类中,即PhoneStatusBar中
PhoneStatusBar的createAndAddWindows():

 @Override
 public void createAndAddWindows() {
        addStatusBarWindow();
 }
 
 private void addStatusBarWindow() {
        makeStatusBarView();//关键方法,创建StatusBarView
        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
 }

可以看到这里最终调用了makeStatusBarView方法:

protected PhoneStatusBarView makeStatusBarView() {
        final Context context = mContext;

        Resources res = context.getResources();

        updateDisplaySize(); // populates mDisplayMetrics
        updateResources();

        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
                R.layout.super_status_bar, null);
        ........
        mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
        mStatusBarView.setBar(this);

        PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
        mStatusBarView.setPanelHolder(holder);

        mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
                R.id.notification_panel);
        mNotificationPanel.setStatusBar(this);

        if (!ActivityManager.isHighEndGfx()) {
            mStatusBarWindow.setBackground(null);
            mNotificationPanel.setBackground(new FastColorDrawable(context.getColor(
                    R.color.notification_panel_solid_background)));
        }
        ........
        
        mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header);
        mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
        mKeyguardBottomArea =
                (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
        ........
        //可以看到这里完成了许多systemui关键组件的view创建。这个方法很重要。
        ........
        // Set up the quick settings tile panel
        mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
        if (mQSPanel != null) {
            final QSTileHost qsh = new QSTileHost(mContext, this,
                    mBluetoothController, mLocationController, mRotationLockController,
                    mNetworkController, mZenModeController, mHotspotController,
                    mCastController, mFlashlightController,
                    mUserSwitcherController, mKeyguardMonitor,
                    mSecurityController,
                    mAudioProfileController
                    );
            mQSPanel.setHost(qsh);
            mQSPanel.setTiles(qsh.getTiles());   
            mHeader.setQSPanel(mQSPanel);
            qsh.setCallback(new QSTileHost.Callback() {
                @Override
                public void onTilesChanged() {
                    mQSPanel.setTiles(qsh.getTiles());
                }
            });
        }
        .........

看到QSPanel,发现了我们的目标,它是下拉状态栏的一个关键类。
在mQSPanel加载xml布局之后,创建QSTileHost对象。
直接去看QSTileHost的继承关系及构造方法:

public class QSTileHost implements QSTile.Host, Tunable {
    .......
    //这里QSTileHost继承了Tunable接口,下文将有一个非常经典的java回调实现


public QSTileHost(Context context, PhoneStatusBar statusBar,

        ........
        
          TunerService.get(mContext).addTunable(this, TILES_SETTING);
          //回调实现的第一步
          //这里传了this。
          //将继承了Tunable接口的QSTileHost传递给TunerService的addTunable();
          
    }

重点即为TunerService的addTunable。TunerService看起来有点眼熟,向上查看文章,它和systembars一起
在SystemUIApplication中被实例化并开启
来看TunerService的addTunable:

public void addTunable(Tunable tunable, String... keys) {
        for (String key : keys) {
            addTunable(tunable, key);
        }
    }

    private void addTunable(Tunable tunable, String key) {
        if (!mTunableLookup.containsKey(key)) {
            mTunableLookup.put(key, new ArrayList<Tunable>());
        }
        mTunableLookup.get(key).add(tunable);
        Uri uri = Settings.Secure.getUriFor(key);
        if (!mListeningUris.containsKey(uri)) {
            mListeningUris.put(uri, key);
            mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
        }
        // Send the first state.
        String value = Settings.Secure.getStringForUser(mContentResolver, key, mCurrentUser);
        //这里的value将获得一个长字符,包含了所有系统QSTitle组件名,在后面进行分割,生成具体的QSTitle对象
        //例子:  wifi,location,dataconnection,hotspot,audioprofile,bt,rotation,airplane,screenshot
        tunable.onTuningChanged(key, value);//回调第二步,addTunable传递来的QSTileHost对象调用自己的onTuningChanged方法
    }

来看QSTileHost的onTuningChanged()

@Override
    public void onTuningChanged(String key, String newValue) {
        if (!TILES_SETTING.equals(key)) {
            return;
        }
        final List<String> tileSpecs = loadTileSpecs(newValue);//切割传递来的newValue为List
        if (tileSpecs.equals(mTileSpecs)) return;
        for (Map.Entry<String, QSTile<?>> tile : mTiles.entrySet()) {
            if (!tileSpecs.contains(tile.getKey())) {
                tile.getValue().destroy();
            }
        }
        final LinkedHashMap<String, QSTile<?>> newTiles = new LinkedHashMap<>();
        for (String tileSpec : tileSpecs) {
            if (mTiles.containsKey(tileSpec)) {
                newTiles.put(tileSpec, mTiles.get(tileSpec));
            } else {
                if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
                try {
                    newTiles.put(tileSpec, createTile(tileSpec));
                    //createTile()中根据上文切割出的QSTitle名创建相应的对象
                } catch (Throwable t) {
                }
            }
        }
        mTileSpecs.clear();
        mTileSpecs.addAll(tileSpecs);
        mTiles.clear();
        mTiles.putAll(newTiles);
        if (mCallback != null) {
            mCallback.onTilesChanged();
        }
    }

看看createTile(tileSpec)

protected QSTile<?> createTile(String tileSpec) {
        if (tileSpec.equals("wifi")) return new WifiTile(this);
        else if (tileSpec.equals("bt")) return new BluetoothTile(this);
        else if (tileSpec.equals("inversion")) return new ColorInversionTile(this);
        else if (tileSpec.equals("cell")) return new CellularTile(this);
        else if (tileSpec.equals("airplane")) return new AirplaneModeTile(this);
        else if (tileSpec.equals("dnd")) return new DndTile(this);
        else if (tileSpec.equals("rotation")) return new RotationLockTile(this);
        else if (tileSpec.equals("flashlight")) return new FlashlightTile(this);
        else if (tileSpec.equals("location")) return new LocationTile(this);
        else if (tileSpec.equals("cast")) return new CastTile(this);
        else if (tileSpec.equals("hotspot")) return new HotspotTile(this);
        else throw new IllegalArgumentException("Bad tile spec: " + tileSpec);
    }

可以看到这里具体创建了QSTitle的各个对象。
那么这些QSTitle对象又是如何加载到QSPanel的View中呢?
而QSPanel在哪加入到了StatusBarHeaderView中。
回头在看看PhoneStatusBar的makeStatusBarView方法:

// Set up the quick settings tile panel
        mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel);
        if (mQSPanel != null) {
            final QSTileHost qsh = new QSTileHost(mContext, this,
                    mBluetoothController, mLocationController, mRotationLockController,
                    mNetworkController, mZenModeController, mHotspotController,
                    mCastController, mFlashlightController,
                    mUserSwitcherController, mKeyguardMonitor,
                    mSecurityController,
                    mAudioProfileController
                    );
            mQSPanel.setHost(qsh);
            mQSPanel.setTiles(qsh.getTiles());   
            mHeader.setQSPanel(mQSPanel);//mHeader->StatusBarHeaderView QSPanel加入到了StatusBarHeaderView中。
            qsh.setCallback(new QSTileHost.Callback() {
                @Override
                public void onTilesChanged() {
                    mQSPanel.setTiles(qsh.getTiles());
                }
            });
        }

刚才我们从QSTileHost的构造函数开始,分析了具体每个QSTitle的实例化。我们现在创建QSTileHost之后,又发生了什么。

mQSPanel.setHost(qsh);//将QSTileHost对象放入QSPanel
mQSPanel.setTiles(qsh.getTiles());
/*QSTileHost获得title用来设置QSPanel的title,从函数名来看,
很像是我们上面问题的答案
*/

看看qsh.getTiles()及mQSPanel.setTiles()

    @Override
    public Collection<QSTile<?>> getTiles() {  
        return mTiles.values();//获得title值
    }
    
    
    public void setTiles(Collection<QSTile<?>> tiles) {
        for (TileRecord record : mRecords) {
            removeView(record.tileView);
        }
        mRecords.clear();
        for (QSTile<?> tile : tiles) {
            addTile(tile);//在这里,具体的每个QStitle在这里被addTile进了QSPanel
        }
        if (isShowingDetail()) {
            mDetail.bringToFront();
        }
    }

看看addTile():

private void addTile(final QSTile<?> tile) {
        final TileRecord r = new TileRecord();
        r.tile = tile;
        r.tileView = tile.createTileView(mContext);
        r.tileView.setVisibility(View.GONE);
        final QSTile.Callback callback = new QSTile.Callback() {
            @Override
            public void onStateChanged(QSTile.State state) {
                if (!r.openingDetail) {
                    drawTile(r, state);
                }
            }
            @Override
            public void onShowDetail(boolean show) {
                QSPanel.this.showDetail(show, r);
            }
            @Override
            public void onToggleStateChanged(boolean state) {
                if (mDetailRecord == r) {
                    fireToggleStateChanged(state);
                }
            }
            @Override
            public void onScanStateChanged(boolean state) {
                r.scanState = state;
                if (mDetailRecord == r) {
                    fireScanStateChanged(r.scanState);
                }
            }

            @Override
            public void onAnnouncementRequested(CharSequence announcement) {
                announceForAccessibility(announcement);
            }
        };
        r.tile.setCallback(callback);
        final View.OnClickListener click = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                r.tile.click();
            }
        };
        final View.OnClickListener clickSecondary = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                r.tile.secondaryClick();
            }
        };
        final View.OnLongClickListener longClick = new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                r.tile.longClick();
                return true;
            }
        };
        r.tileView.init(click, clickSecondary, longClick);
        r.tile.setListening(mListening);
        callback.onStateChanged(r.tile.getState());
        r.tile.refreshState();
        mRecords.add(r);

        addView(r.tileView);//加载进QSPanel
    }

addTile()中创建了一个callback,实际运行中Title刷新,点击事件等许多操作都将与这个callback挂钩
同时,最后addView函数也将这个QSTitle加载进了QSPanel中。
至此,文章告一段落。

参考文章:
Android SystemServer 启动流程
Android之SystemUI加载流程和NavigationBar的分析
http://www.2cto.com/kf/201604/499625.html
Android 6.0 系统学习之 Zygote
http://www.open-open.com/lib/view/open1449567150379.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容