android4 SystemUI 流程分析

什么是SystemUI?

对于Phone来说SystemUI指的是:StatusBar(状态栏)、NavigationBar(导航栏)。而对于Tablet或者是TV来说SystemUI指的是:
CombinedBar(包括了StatusBar和NavigationBar)。

启动后Phone界面上的信号,蓝牙标志,Wifi标志等等这些状态显示标志都会在StatusBar上显示。当我们的设备开机后,首先
需要给用户呈现的就是各种界面同时也包括了我们的SystemUI,因此对于整个Android系统来说,SystemUI都有举足轻重的作用。

现在就从代码开始一步步的分析

1、启动流程

代码路径:fameworks/base/packages/SystemUI

建立工程导入到eclipse中代码具体图示:

先从 AndroidManifest.xml 看看有哪些东东,以前说过Android中有四大组件,这里就有如下的三大部分:

系统服务 Service :

SystemUIService
TakeScreenshotService
LoadAverageService

广播接收器 BroadcastReceive:

BootReceiver

Activity 应用:

USB的挺多哟...
UsbStorageActivity
UsbConfirmActivity
UsbPermissionActivity
UsbStorageActivity
UsbAccessoryUriActivity

NetworkOverLimitActivity

<!-- started from ... somewhere -->
Nyandroid

具体定义请看 AndroidManifest.xml 文件,上面只是简单的列一下

先看第一个Activity -- Nyandroid 这里做了什么呢?
就是网上传说中的 好多安卓机器人飞过去。。。。其中代码很简单,简单说一下动画效果的代码:

  1. <span style="font-size:14px">public class FlyingCat extends ImageView {
  2. public FlyingCat(Context context, AttributeSet as) {
  3. super(context, as);
  4. setImageResource(R.drawable.nyandroid_anim); // @@@
  5. if (DEBUG) setBackgroundColor(0x80FF0000);
  6. }
  7. ...
  8. }</span>

定义在 frameworks\base\packages\SystemUI\res\drawable\nyandroid_anim.xml

  1. <span style="font-size:14px"><animation-list
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:oneshot="false">
  4. <item android:drawable="@drawable/nyandroid00" android:duration="80" />
  5. <item android:drawable="@drawable/nyandroid01" android:duration="80" />
  6. <item android:drawable="@drawable/nyandroid02" android:duration="80" />
  7. <item android:drawable="@drawable/nyandroid03" android:duration="80" />
  8. <item android:drawable="@drawable/nyandroid04" android:duration="80" />
  9. <item android:drawable="@drawable/nyandroid05" android:duration="80" />
  10. <item android:drawable="@drawable/nyandroid06" android:duration="80" />
  11. <item android:drawable="@drawable/nyandroid07" android:duration="80" />
  12. <item android:drawable="@drawable/nyandroid08" android:duration="80" />
  13. <item android:drawable="@drawable/nyandroid09" android:duration="80" />
  14. <item android:drawable="@drawable/nyandroid10" android:duration="80" />
  15. <item android:drawable="@drawable/nyandroid11" android:duration="80" />
  16. </animation-list></span>

相关图片在: frameworks\base\packages\SystemUI\res\drawable-nodpi  如图示:

然后再看最重要的服务:SystemUIService

一般来说,Service启动一般由开机广播或者StartService/BindService这几种方式来启动。既然这个Service是一个系统
服务,应该是由系统这边启动,那么看下 SystemServer.java ,果然发现如下启动代码:

  1. <span style="font-size:14px">startSystemUi(contextF);
  2. static final void startSystemUi(Context context) {
  3. Intent intent = new Intent();
  4. intent.setComponent(new ComponentName("com.android.systemui",
  5. "com.android.systemui.SystemUIService"));
  6. Slog.d(TAG, "Starting service: " + intent);
  7. context.startService(intent);
  8. }</span>

对于Android启动流程请看如下系统文章:

http://blog.csdn.net/andyhuabing/article/details/7346203  android启动--深入理解init进程
http://blog.csdn.net/andyhuabing/article/details/7349986  android启动--深入理解zygote
http://blog.csdn.net/andyhuabing/article/details/7351691  android启动--深入理解zygote (II)
http://blog.csdn.net/andyhuabing/article/details/7353910  android启动--深入理解启动HOME

那么就继续跟踪 SystemUIService 中代码:

  1. <span style="font-size:14px">/**
  2. * The class names of the stuff to start.
  3. */
  4. final Object[] SERVICES = new Object[] {
  5. 0, // system bar or status bar, filled in below.
  6. com.android.systemui.power.PowerUI.class,
  7. };
  8. @Override
  9. public void onCreate() {
  10. // Pick status bar or system bar.
  11. IWindowManager wm = IWindowManager.Stub.asInterface(
  12. ServiceManager.getService(Context.WINDOW_SERVICE));
  13. try {
  14. SERVICES[0] = wm.canStatusBarHide()
  15. ? R.string.config_statusBarComponent
  16. : R.string.config_systemBarComponent;
  17. } catch (RemoteException e) {
  18. Slog.w(TAG, "Failing checking whether status bar can hide", e);
  19. }
  20. final int N = SERVICES.length;
  21. mServices = new SystemUI[N];
  22. for (int i=0; i<N; i++) {
  23. Class cl = chooseClass(SERVICES[i]);
  24. Slog.d(TAG, "loading: " + cl);
  25. try {
  26. mServices[i] = (SystemUI)cl.newInstance();
  27. } catch (IllegalAccessException ex) {
  28. throw new RuntimeException(ex);
  29. } catch (InstantiationException ex) {
  30. throw new RuntimeException(ex);
  31. }
  32. mServices[i].mContext = this;
  33. Slog.d(TAG, "running: " + mServices[i]);
  34. mServices[i].start();
  35. }
  36. }</span>

在这代码中:

  1. <span style="font-size:14px">SERVICES[0] = wm.canStatusBarHide()
  2. ? R.string.config_statusBarComponent
  3. : R.string.config_systemBarComponent;
  4. </span>

通过AIDL获取WindowManager对象并调用 wm.canStatusBarHide() 这个代码在哪里呢?

查看: frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java

  1. <span style="font-size:14px">    public boolean canStatusBarHide() {
  2. return mStatusBarCanHide;
  3. }
  4. public void setInitialDisplaySize(int width, int height) {
  5. ...
  6. // Determine whether the status bar can hide based on the size
  7. // of the screen.  We assume sizes > 600dp are tablets where we
  8. // will use the system bar.
  9. int shortSizeDp = shortSize
  10. * DisplayMetrics.DENSITY_DEFAULT
  11. / DisplayMetrics.DENSITY_DEVICE;
  12. mStatusBarCanHide = shortSizeDp < 600;
  13. }</span>

从以上代码来看,shortSizeDp小于600dp时,则系统会认为该设备是Phone反之则认为是Tablet。
根据mStatusBarCanHide的值,设定StatusBar或者SystemBar(CombinedBar)的高度,以及是否显示NavigationBar。

2、StatusBar(状态栏)及NavigationBar(导航栏)

如果是 StatusBar 则 SERVICES[0] 存放 com.android.systemui.statusbar.phone.PhoneStatusBar 否则存放 
com.android.systemui.statusbar.tablet.TabletStatusBar 
SERVICES[1] 存放 com.android.systemui.power.PowerUI.class

从我的机器上打印来看,
E/SystemServer( 1299): Starting service: Intent { cmp=com.android.systemui/.SystemUIService }

D/SystemUIService( 1382): running: com.android.systemui.statusbar.tablet.TabletStatusBar@415b8b20
D/SystemUIService( 1382): running: com.android.systemui.power.PowerUI@416b5ae8
I/PowerUI ( 1382): start

然后调用 mServices[i].start();那么就分析 TabletStatusBar 中的start方法吧

  1. <span style="font-size:14px">    @Override
  2. public void start() {
  3. super.start(); // will add the main bar view
  4. }</span>

调用到 frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.Java

  1. <span style="font-size:14px">public void start() {
  2. // First set up our views and stuff.
  3. View sb = makeStatusBarView();
  4. // Connect in to the status bar manager service
  5. StatusBarIconList iconList = new StatusBarIconList();
  6. ArrayList<IBinder> notificationKeys = new ArrayList<IBinder>();
  7. ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
  8. mCommandQueue = new CommandQueue(this, iconList);
  9. mBarService = IStatusBarService.Stub.asInterface(
  10. ServiceManager.getService(Context.STATUS_BAR_SERVICE));
  11. int[] switches = new int[7];
  12. ArrayList<IBinder> binders = new ArrayList<IBinder>();
  13. try {
  14. mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
  15. switches, binders);
  16. } catch (RemoteException ex) {
  17. // If the system process isn't there we're doomed anyway.
  18. }
  19. disable(switches[0]);
  20. setSystemUiVisibility(switches[1]);
  21. topAppWindowChanged(switches[2] != 0);
  22. // StatusBarManagerService has a back up of IME token and it's restored here.
  23. setImeWindowStatus(binders.get(0), switches[3], switches[4]);
  24. setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
  25. // Set up the initial icon state
  26. int N = iconList.size();
  27. int viewIndex = 0;
  28. for (int i=0; i<N; i++) {
  29. StatusBarIcon icon = iconList.getIcon(i);
  30. if (icon != null) {
  31. addIcon(iconList.getSlot(i), i, viewIndex, icon);
  32. viewIndex++;
  33. }
  34. }
  35. // Set up the initial notification state
  36. N = notificationKeys.size();
  37. if (N == notifications.size()) {
  38. for (int i=0; i<N; i++) {
  39. addNotification(notificationKeys.get(i), notifications.get(i));
  40. }
  41. } else {
  42. Log.wtf(TAG, "Notification list length mismatch: keys=" + N
  43. + " notifications=" + notifications.size());
  44. }
  45. // Put up the view
  46. final int height = getStatusBarHeight();
  47. final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
  48. ViewGroup.LayoutParams.MATCH_PARENT,
  49. height,
  50. WindowManager.LayoutParams.TYPE_STATUS_BAR,
  51. WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
  52. | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
  53. | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
  54. PixelFormat.OPAQUE);
  55. // the status bar should be in an overlay if possible
  56. final Display defaultDisplay
  57. = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
  58. .getDefaultDisplay();
  59. if (ActivityManager.isHighEndGfx(defaultDisplay)) {
  60. lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
  61. }
  62. lp.gravity = getStatusBarGravity();
  63. lp.setTitle("StatusBar");
  64. lp.packageName = mContext.getPackageName();
  65. lp.windowAnimations = R.style.Animation_StatusBar;
  66. WindowManagerImpl.getDefault().addView(sb, lp);
  67. if (SPEW) {
  68. Slog.d(TAG, "Added status bar view: gravity=0x" + Integer.toHexString(lp.gravity)
  69. + " icons=" + iconList.size()
  70. + " disabled=0x" + Integer.toHexString(switches[0])
  71. + " lights=" + switches[1]
  72. + " menu=" + switches[2]
  73. + " imeButton=" + switches[3]
  74. );
  75. }
  76. mDoNotDisturb = new DoNotDisturb(mContext);
  77. }</span>

在这里,完成了SystemUI的整个初始化以及设置过程,并最终呈现到界面上。
启动过程中完成如下操作:
1、获取icon list,addIcon(iconList.getSlot(i), i, viewIndex, icon);
2、获取notification,addNotification(notificationKeys.get(i), notifications.get(i));
3、显示StatusBar,WindowManagerImpl.getDefault().addView(sb, lp);
   显示NavigationBar,WindowManagerImpl.getDefault().addView(
                mNavigationBarView, getNavigationBarLayoutParams());

时序图如下:

3、最近任务缩略图显示 


长按home键,列出最近启动过的任务缩略图,重要的两个类

// Recent apps
private RecentsPanelView mRecentsPanel;
private RecentTasksLoader mRecentTasksLoader;

SystemUI 获取按键事件,获取缩略图并将其显示出来,最后响应view上按键响应相应事件:

对于我们来说,关注点主要有如下几个:

1、缩略图如何获取

  1. <span style="font-size:14px">RecentsPanelView.java 中
  2. refreshRecentTasksList(recentTaskDescriptions);
  3. -->
  4. mRecentTaskDescriptions = mRecentTasksLoader.getRecentTasks();
  5. -->
  6. RecentTasksLoader.java 中
  7. // return a snapshot of the current list of recent apps
  8. ArrayList<TaskDescription> getRecentTasks() {
  9. cancelLoadingThumbnails();
  10. ArrayList<TaskDescription> tasks = new ArrayList<TaskDescription>();
  11. final PackageManager pm = mContext.getPackageManager();
  12. final ActivityManager am = (ActivityManager)
  13. mContext.getSystemService(Context.ACTIVITY_SERVICE);
  14. final List<ActivityManager.RecentTaskInfo> recentTasks =
  15. am.getRecentTasks(MAX_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);
  16. ActivityInfo homeInfo = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
  17. .resolveActivityInfo(pm, 0);
  18. HashSet<Integer> recentTasksToKeepInCache = new HashSet<Integer>();
  19. int numTasks = recentTasks.size();
  20. // skip the first task - assume it's either the home screen or the current activity.
  21. final int first = 1;
  22. recentTasksToKeepInCache.add(recentTasks.get(0).persistentId);
  23. for (int i = first, index = 0; i < numTasks && (index < MAX_TASKS); ++i) {
  24. final ActivityManager.RecentTaskInfo recentInfo = recentTasks.get(i);
  25. TaskDescription item = createTaskDescription(recentInfo.id,
  26. recentInfo.persistentId, recentInfo.baseIntent,
  27. recentInfo.origActivity, recentInfo.description, homeInfo);
  28. if (item != null) {
  29. tasks.add(item);
  30. ++index;
  31. }
  32. }
  33. // when we're not using the TaskDescription cache, we load the thumbnails in the
  34. // background
  35. loadThumbnailsInBackground(new ArrayList<TaskDescription>(tasks));
  36. return tasks;
  37. }
  38. </span>

这里利用 ActivityManager 中的方法:getRecentTasks 获取当前任务的列表,然后再利用 getTaskThumbnails 获取

按键View  就是几个按键相应的View

  1. <span style="font-size:14px">    public View getRecentsButton() {
  2. return mCurrentView.findViewById(R.id.recent_apps);
  3. }
  4. public View getMenuButton() {
  5. return mCurrentView.findViewById(R.id.menu);
  6. }
  7. public View getBackButton() {
  8. return mCurrentView.findViewById(R.id.back);
  9. }
  10. public View getHomeButton() {
  11. return mCurrentView.findViewById(R.id.home);
  12. }</span>

相应的应用缩略图,调用序列图如下:

2、显示缩略图
public void show(boolean show, boolean animate,
            ArrayList<TaskDescription> recentTaskDescriptions) {
        if (show) {
            // Need to update list of recent apps before we set visibility so this view's
            // content description is updated before it gets focus for TalkBack mode
            refreshRecentTasksList(recentTaskDescriptions);

// if there are no apps, either bring up a "No recent apps" message, or just
            // quit early
            boolean noApps = (mRecentTaskDescriptions.size() == 0);
            if (mRecentsNoApps != null) { // doesn't exist on large devices
                mRecentsNoApps.setVisibility(noApps ? View.VISIBLE : View.INVISIBLE);
            } else {
                if (noApps) {
                    if (DEBUG) Log.v(TAG, "Nothing to show");
                    return;
                }
            }
         }else {
            mRecentTasksLoader.cancelLoadingThumbnails();
            mRecentTasksDirty = true;
         }
         ...
}

如果 mRecentsNoApps 为空则表示没有任务,显示 "No recent apps" 否则显示应用列表
否则则显示任务的缩略图。时序图如下:

3、点击某个缩略图执行

这里分为点击某个缩略图执行程序及长按缩略图执行程序

这里直接继承了 View.OnItemClickListener 所以可以直接执行子项按键事件

  1. public class RecentsPanelView extends RelativeLayout implements OnItemClickListener, RecentsCallback,
  2. StatusBarPanel, Animator.AnimatorListener, View.OnTouchListener

处理点击事件方法:

  1. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  2. handleOnClick(view);
  3. }
  4. public void handleOnClick(View view) {
  5. TaskDescription ad = ((ViewHolder) view.getTag()).taskDescription;
  6. final Context context = view.getContext();
  7. final ActivityManager am = (ActivityManager)
  8. context.getSystemService(Context.ACTIVITY_SERVICE);
  9. if (ad.taskId >= 0) {
  10. // This is an active task; it should just go to the foreground.
  11. am.moveTaskToFront(ad.taskId, ActivityManager.MOVE_TASK_WITH_HOME);
  12. } else {
  13. Intent intent = ad.intent;
  14. intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
  15. | Intent.FLAG_ACTIVITY_TASK_ON_HOME
  16. | Intent.FLAG_ACTIVITY_NEW_TASK);
  17. if (DEBUG) Log.v(TAG, "Starting activity " + intent);
  18. context.startActivity(intent);
  19. }
  20. hide(true);
  21. }

注意代码:context.startActivity(intent);  这里就是执行对应的 Activity

处理长按键点击事件方法:

  1. public void handleLongPress(
  2. final View selectedView, final View anchorView, final View thumbnailView) {
  3. thumbnailView.setSelected(true);
  4. PopupMenu popup = new PopupMenu(mContext, anchorView == null ? selectedView : anchorView);
  5. popup.getMenuInflater().inflate(R.menu.recent_popup_menu, popup.getMenu());
  6. popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
  7. public boolean onMenuItemClick(MenuItem item) {
  8. if (item.getItemId() == R.id.recent_remove_item) {
  9. mRecentsContainer.removeViewInLayout(selectedView);
  10. } else if (item.getItemId() == R.id.recent_inspect_item) {
  11. ViewHolder viewHolder = (ViewHolder) selectedView.getTag();
  12. if (viewHolder != null) {
  13. final TaskDescription ad = viewHolder.taskDescription;
  14. startApplicationDetailsActivity(ad.packageName);
  15. mBar.animateCollapse();
  16. } else {
  17. throw new IllegalStateException("Oops, no tag on view " + selectedView);
  18. }
  19. } else {
  20. return false;
  21. }
  22. return true;
  23. }
  24. });
  25. popup.setOnDismissListener(new PopupMenu.OnDismissListener() {
  26. public void onDismiss(PopupMenu menu) {
  27. thumbnailView.setSelected(false);
  28. }
  29. });
  30. popup.show();
  31. }

这里弹出一个PopupMenu,分别是 A:"Remove from list" 及 B:"App Info"

其中A项表示将此任务移除出列表,执行 mRecentsContainer.removeViewInLayout(selectedView);

另外B是启动另外一个Acitivty列出应用信息:

  1. private void startApplicationDetailsActivity(String packageName) {
  2. Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
  3. Uri.fromParts("package", packageName, null));
  4. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  5. getContext().startActivity(intent);
  6. }

总结:

这里详细的对SystemUI 的两个最重要的 StatusBar NavigationBar(SystemUIService) 及缩略图代码流程分析。
因此各家厂商根据自家的需求,需要定制SystemUI或者美化SystemUI,不同的平台也会有不同的修改,但大体框架是没有变的,
无非是在原有基础上的修修改改或者增加一些自己的类等等。

【转】android SystemUI 流程分析的更多相关文章

  1. Android SystemUI源代码分析和修改

    1.在导航栏中添加音量加减button 一些Android音量调节button.或者从保护实体按键的角度考虑,就须要在导航栏的虚拟按键中加入音量加减调节按键. 效果例如以下图所看到的: 实现步骤例如以 ...

  2. Android 启动流程分析

    原文:https://www.jianshu.com/p/a5532ecc8377 作者曾经在高通的Android性能组工作,主要工作是优化Android Application的启动时间. APP基 ...

  3. Qt for Android 部署流程分析

    原地址:http://blog.csdn.net/foruok/article/details/17796017 今天为了测试使用 Qt Creator 3.0.0 开发的纯 C 工程,利用了 Win ...

  4. android PakageManagerService启动流程分析

    PakageManagerService的启动流程图 1.PakageManagerService概述 PakageManagerService是android系统中一个核心的服务,它负责系统中Pac ...

  5. Android 4.4KitKat AudioRecord 流程分析

    Android是架构分为三层: 底层      Linux Kernel 中间层  主要由C++实现 (Android 60%源码都是C++实现) 应用层  主要由JAVA开发的应用程序 应用程序执行 ...

  6. Android 4.4 音量调节流程分析(二)

    之前在Android 4.4 音量调节流程分析(一)里已经有简单的分析音量控制的流程,今天想接着继续分析下音量大小计算的方法.对于任一播放文件而言其本身都有着固定大小的音量Volume_Max,而在A ...

  7. Cocos2d-x3.3RC0的Android编译Activity启动流程分析

    本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2 ...

  8. Android bluetooth介绍(四): a2dp connect流程分析

    关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_ ...

  9. Android 7.1 屏幕旋转流程分析

    Android 7.1   屏幕旋转流程分析 一.概述 Android屏幕的旋转在framework主要涉及到三个类,结构如图 PhoneWindowManager:为屏幕的横竖屏转换的管理类. Wi ...

随机推荐

  1. FastJson的忽略字段和格式日期用法

     1.指定序列化顺序 缺省fastjson序列化一个java bean,是根据fieldName的字母序进行序列化的,你可以通过ordinal指定字段的顺序.这个特性需要1.1.42以上版本. pub ...

  2. 【学习笔记】--- 老男孩学Python,day18 面向对象------ 属性,类方法,静态方法

    属性 属性: 将方法伪装成一个属性,代码上没有什么提升,只是更合理. 应用场景: 类中 要用名词时候可以用@property  比如,求面积,周长,平方,体脂 等运算时候 例如:   bmi是名词,最 ...

  3. Spring 中面向AOP之一系列做法

    Spring的AOP实现是通过集成AspectJ框架实现的. 想必这句话大家都知道了吧,不知道也没关系,最起码你现在知道了. 四种实现方案,接下来我们一一去揭开它的神秘的面纱....... 第一种(伪 ...

  4. How To Improve Deep Learning Performance

    如何提高深度学习性能 20 Tips, Tricks and Techniques That You Can Use ToFight Overfitting and Get Better Genera ...

  5. sql 内连接 子查询 合并查询

    -- 内连接:-- 显示员工姓名.工资和公司所在地 select e.ename, e.sal, d.dname from emp e,dept d; -- 笛卡尔积 select e.ename, ...

  6. 八、Vue中的computed属性

    看了网上很多资料,对vue的computed讲解自己看的都不是很清晰,今天忙里抽闲,和同事们又闲聊起来,对computed这个属性才有了一个稍微比较清晰的认识,下面的文章有一部分是转自: https: ...

  7. WiFi 干扰器,有时间可以去试试呦!

    转自社区: 0X01 引言 想不想搞个WIFI干扰器?网上搜集了一下资料,发现用esp8266可以实现简单的干扰功能,包括断网.复制.欺骗等等.刚好手上有块Tpyboard V202(30元),也是e ...

  8. Azure 镜像市场发布商指南

    Azure 镜像市场发布商指南 本指南提供独立软件供应商产品上架到 Azure 镜像市场(以下简称 Azure 镜像市场)需要遵循的全流程. 文档适用范围 本指南适用于希望通过由世纪互联运营的Micr ...

  9. MVC技术的面试问题

    MVC中的三种方式: ORM框架:对象关系映射关系 ,面向对象的对象模型和关系型数据之间的相互转换.基于关系型数据库的数据存储,实现一个虚拟的面向对象的数据访问接口.只要提供了持久化类与表的映射关系, ...

  10. C语言高精度乘法

    #include <stdio.h> void highPrecision (int N ); ] = {, }, length = ; //开辟一个大的数组,全局变量length记录长度 ...