Android系统默认Home应用程序 Launcher 的启动过程源代码分析

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

               

        在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应用程序就是Launcher了,本文将详细分析Launcher应用程序的启动过程。

《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

        Android系统的Home应用程序Launcher是由ActivityManagerService启动的,而ActivityManagerService和PackageManagerService一样,都是在开机时由SystemServer组件启动的,SystemServer组件首先是启动ePackageManagerServic,由它来负责安装系统的应用程序,具体可以参考前面一篇文章Android应用程序安装过程源代码分析,系统中的应用程序安装好了以后,SystemServer组件接下来就要通过ActivityManagerService来启动Home应用程序Launcher了,Launcher在启动的时候便会通过PackageManagerServic把系统中已经安装好的应用程序以快捷图标的形式展示在桌面上,这样用户就可以使用这些应用程序了,整个过程如下图所示:


点击查看大图

        下面详细分析每一个步骤。

        Step 1. SystemServer.main

        这个函数定义在frameworks/base/services/java/com/android/server/SystemServer.java文件中,具体可以参考前面一篇文章Android应用程序安装过程源代码分析的Step 1。

        Step 2. SystemServer.init1

        这个函数是一个JNI方法,实现在 frameworks/base/services/jni/com_android_server_SystemServer.cpp文件中,具体可以参考前面一篇文章Android应用程序安装过程源代码分析的Step 2。

        Step 3. libsystem_server.system_init

        函数system_init实现在libsystem_server库中,源代码位于frameworks/base/cmds/system_server/library/system_init.cpp文件中,具体可以参考前面一篇文章Android应用程序安装过程源代码分析的Step 3。

        Step 4. AndroidRuntime.callStatic

        这个函数定义在frameworks/base/core/jni/AndroidRuntime.cpp文件中,具体可以参考前面一篇文章Android应用程序安装过程源代码分析的Step 4。

        Step 5. SystemServer.init2

        这个函数定义在frameworks/base/services/java/com/android/server/SystemServer.java文件中,具体可以参考前面一篇文章Android应用程序安装过程源代码分析的Step 5。

        Step 6. ServerThread.run

        这个函数定义在frameworks/base/services/java/com/android/server/SystemServer.java文件中,具体可以参考前面一篇文章Android应用程序安装过程源代码分析的Step 6。

        Step 7. ActivityManagerService.main

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:

public final class ActivityManagerService extends ActivityManagerNative  implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... public static final Context main(int factoryTest) {  AThread thr = new AThread();  thr.start();  synchronized (thr) {   while (thr.mService == null) {    try {     thr.wait();    } catch (InterruptedException e) {    }   }  }  ActivityManagerService m = thr.mService;  mSelf = m;  ActivityThread at = ActivityThread.systemMain();  mSystemThread = at;  Context context = at.getSystemContext();  m.mContext = context;  m.mFactoryTest = factoryTest;  m.mMainStack = new ActivityStack(m, context, true);  m.mBatteryStatsService.publish(context);  m.mUsageStatsService.publish(context);  synchronized (thr) {   thr.mReady = true;   thr.notifyAll();  }  m.startRunning(null, null, null, null);  return context; } ......}
        这个函数首先通过AThread线程对象来内部创建了一个ActivityManagerService实例,然后将这个实例保存其成员变量mService中,接着又把这个ActivityManagerService实例保存在ActivityManagerService类的静态成员变量mSelf中,最后初始化其它成员变量,就结束了。

        Step 8. PackageManagerService.main

        这个函数定义在frameworks/base/services/java/com/android/server/PackageManagerService.java文件中,具体可以参考前面一篇文章Android应用程序安装过程源代码分析的Step 7。执行完这一步之后,系统中的应用程序的所有信息都保存在PackageManagerService中了,后面Home应用程序Launcher启动起来后,就会把PackageManagerService中的应用程序信息取出来,然后以快捷图标的形式展示在桌面上,后面我们将会看到这个过程。

        Step 9. ActivityManagerService.setSystemProcess

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:

public final class ActivityManagerService extends ActivityManagerNative  implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... public static void setSystemProcess() {  try {   ActivityManagerService m = mSelf;   ServiceManager.addService("activity", m);   ServiceManager.addService("meminfo", new MemBinder(m));   if (MONITOR_CPU_USAGE) {    ServiceManager.addService("cpuinfo", new CpuBinder(m));   }   ServiceManager.addService("permission", new PermissionController(m));   ApplicationInfo info =    mSelf.mContext.getPackageManager().getApplicationInfo(    "android", STOCK_PM_FLAGS);   mSystemThread.installSystemApplicationInfo(info);   synchronized (mSelf) {    ProcessRecord app = mSelf.newProcessRecordLocked(     mSystemThread.getApplicationThread(), info,     info.processName);    app.persistent = true;    app.pid = MY_PID;    app.maxAdj = SYSTEM_ADJ;    mSelf.mProcessNames.put(app.processName, app.info.uid, app);    synchronized (mSelf.mPidsSelfLocked) {     mSelf.mPidsSelfLocked.put(app.pid, app);    }    mSelf.updateLruProcessLocked(app, true, true);   }  } catch (PackageManager.NameNotFoundException e) {   throw new RuntimeException(    "Unable to find android system package", e);  } } ......}
        这个函数首先是将这个ActivityManagerService实例添加到ServiceManager中去托管,这样其它地方就可以通过ServiceManager.getService接口来访问这个全局唯一的ActivityManagerService实例了,接着又通过调用mSystemThread.installSystemApplicationInfo函数来把应用程序框架层下面的android包加载进来 ,这里的mSystemThread是一个ActivityThread类型的实例变量,它是在上面的Step 7中创建的,后面就是一些其它的初始化工作了。

        Step 10.  ActivityManagerService.systemReady

        这个函数是在上面的Step 6中的ServerThread.run函数在将系统中的一系列服务都初始化完毕之后才调用的,它定义在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:

public final class ActivityManagerService extends ActivityManagerNative  implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... public void systemReady(final Runnable goingCallback) {  ......  synchronized (this) {   ......   mMainStack.resumeTopActivityLocked(null);  } } ......}
        这个函数的内容比较多,这里省去无关的部分,主要关心启动Home应用程序的逻辑,这里就是通过mMainStack.resumeTopActivityLocked函数来启动Home应用程序的了,这里的mMainStack是一个ActivityStack类型的实例变量。

        Step 11. ActivityStack.resumeTopActivityLocked

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中:

public class ActivityStack { ...... final boolean resumeTopActivityLocked(ActivityRecord prev) {  // Find the first activity that is not finishing.  ActivityRecord next = topRunningActivityLocked(null);  ......  if (next == null) {   // There are no more activities!  Let's just start up the   // Launcher...   if (mMainStack) {    return mService.startHomeActivityLocked();   }  }  ...... } ......}
        这里调用函数topRunningActivityLocked返回的是当前系统Activity堆栈最顶端的Activity,由于此时还没有Activity被启动过,因此,返回值为null,即next变量的值为null,于是就调用mService.startHomeActivityLocked语句,这里的mService就是前面在Step 7中创建的ActivityManagerService实例了。

        Step 12. ActivityManagerService.startHomeActivityLocked

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityManagerServcie.java文件中:

public final class ActivityManagerService extends ActivityManagerNative  implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { ...... boolean startHomeActivityLocked() {  ......  Intent intent = new Intent(   mTopAction,   mTopData != null ? Uri.parse(mTopData) : null);  intent.setComponent(mTopComponent);  if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {   intent.addCategory(Intent.CATEGORY_HOME);  }  ActivityInfo aInfo =   intent.resolveActivityInfo(mContext.getPackageManager(),   STOCK_PM_FLAGS);  if (aInfo != null) {   intent.setComponent(new ComponentName(    aInfo.applicationInfo.packageName, aInfo.name));   // Don't do this if the home app is currently being   // instrumented.   ProcessRecord app = getProcessRecordLocked(aInfo.processName,    aInfo.applicationInfo.uid);   if (app == null || app.instrumentationClass == null) {    intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);    mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,     null, null, 0, 0, 0, false, false);   }  }  return true; } ......}
        函数首先创建一个CATEGORY_HOME类型的Intent,然后通过Intent.resolveActivityInfo函数向PackageManagerService查询Category类型为HOME的Activity,这里我们假设只有系统自带的Launcher应用程序注册了HOME类型的Activity(见packages/apps/Launcher2/AndroidManifest.xml文件):

<manifest    xmlns:android="http://schemas.android.com/apk/res/android"    package="com.android.launcher"    android:sharedUserId="@string/sharedUserId" >    ...... <application     android:name="com.android.launcher2.LauncherApplication"     android:process="@string/process"     android:label="@string/application_name"     android:icon="@drawable/ic_launcher_home">  <activity   android:name="com.android.launcher2.Launcher"   android:launchMode="singleTask"   android:clearTaskOnLaunch="true"   android:stateNotNeeded="true"   android:theme="@style/Theme"   android:screenOrientation="nosensor"   android:windowSoftInputMode="stateUnspecified|adjustPan">   <intent-filter>    <action android:name="android.intent.action.MAIN" />    <category android:name="android.intent.category.HOME" />    <category android:name="android.intent.category.DEFAULT" />    <category android:name="android.intent.category.MONKEY"/>    </intent-filter>  </activity>  ...... </application></manifest>

        因此,这里就返回com.android.launcher2.Launcher这个Activity了。由于是第一次启动这个Activity,接下来调用函数getProcessRecordLocked返回来的ProcessRecord值为null,于是,就调用mMainStack.startActivityLocked函数启动com.android.launcher2.Launcher这个Activity了,这里的mMainStack是一个ActivityStack类型的成员变量。

        Step 13.  ActivityStack.startActivityLocked

        这个函数定义在frameworks/base/services/java/com/android/server/am/ActivityStack.java文件中,具体可以参考Android应用程序启动过程源代码分析一文,这里就不详述了,在我们这个场景中,调用这个函数的最后结果就是把com.android.launcher2.Launcher启动起来,接着调用它的onCreate函数。

        Step 14. Launcher.onCreate

        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/Launcher.java文件中:

public final class Launcher extends Activity  implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher { ...... @Override protected void onCreate(Bundle savedInstanceState) {  ......  if (!mRestoring) {   mModel.startLoader(this, true);  }  ...... } ......}
        这里的mModel是一个LauncherModel类型的成员变量,这里通过调用它的startLoader成员函数来执行加应用程序的操作。

        Step 15. LauncherModel.startLoader

        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:

public class LauncherModel extends BroadcastReceiver { ...... public void startLoader(Context context, boolean isLaunching) {  ......                synchronized (mLock) {                     ......                     // Don't bother to start the thread if we know it's not going to do anything                     if (mCallbacks != null && mCallbacks.get() != null) {                         // If there is already one running, tell it to stop.                         LoaderTask oldTask = mLoaderTask;                         if (oldTask != null) {                             if (oldTask.isLaunching()) {                                 // don't downgrade isLaunching if we're already running                                 isLaunching = true;                             }                             oldTask.stopLocked();           }           mLoaderTask = new LoaderTask(context, isLaunching);           sWorker.post(mLoaderTask);             }        } } ......}
        这里不是直接加载应用程序,而是把加载应用程序的操作作为一个消息来处理。这里的sWorker是一个Handler,通过它的post方式把一个消息放在消息队列中去,然后系统就会调用传进去的参数mLoaderTask的run函数来处理这个消息,这个mLoaderTask是LoaderTask类型的实例,于是,下面就会执行LoaderTask类的run函数了。

        Step 16. LoaderTask.run

        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:

public class LauncherModel extends BroadcastReceiver { ...... private class LoaderTask implements Runnable {  ......  public void run() {   ......   keep_running: {    ......    // second step    if (loadWorkspaceFirst) {     ......     loadAndBindAllApps();    } else {     ......    }    ......   }   ......  }  ...... } ......}
        这里调用loadAndBindAllApps成员函数来进一步操作。

        Step 17. LoaderTask.loadAndBindAllApps
        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:

public class LauncherModel extends BroadcastReceiver { ...... private class LoaderTask implements Runnable {  ......  private void loadAndBindAllApps() {   ......   if (!mAllAppsLoaded) {    loadAllAppsByBatch();    if (mStopped) {     return;    }    mAllAppsLoaded = true;   } else {    onlyBindAllApps();   }  }  ...... } ......}
        由于还没有加载过应用程序,这里的mAllAppsLoaded为false,于是就继续调用loadAllAppsByBatch函数来进一步操作了。

        Step 18. LoaderTask.loadAllAppsByBatch
        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/LauncherModel.java文件中:

public class LauncherModel extends BroadcastReceiver { ...... private class LoaderTask implements Runnable {  ......  private void loadAllAppsByBatch() {    ......   final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);   mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);   final PackageManager packageManager = mContext.getPackageManager();   List<ResolveInfo> apps = null;   int N = Integer.MAX_VALUE;   int startIndex;   int i=0;   int batchSize = -1;   while (i < N && !mStopped) {    if (i == 0) {     mAllAppsList.clear();     ......     apps = packageManager.queryIntentActivities(mainIntent, 0);          ......     N = apps.size();          ......     if (mBatchSize == 0) {      batchSize = N;     } else {      batchSize = mBatchSize;     }     ......     Collections.sort(apps,      new ResolveInfo.DisplayNameComparator(packageManager));    }    startIndex = i;    for (int j=0; i<N && j<batchSize; j++) {     // This builds the icon bitmaps.     mAllAppsList.add(new ApplicationInfo(apps.get(i), mIconCache));     i++;    }    final boolean first = i <= batchSize;    final Callbacks callbacks = tryGetCallbacks(oldCallbacks);    final ArrayList<ApplicationInfo> added = mAllAppsList.added;    mAllAppsList.added = new ArrayList<ApplicationInfo>();       mHandler.post(new Runnable() {     public void run() {      final long t = SystemClock.uptimeMillis();      if (callbacks != null) {       if (first) {        callbacks.bindAllApplications(added);       } else {        callbacks.bindAppsAdded(added);       }       ......      } else {       ......      }     }    });    ......   }   ......  }  ...... } ......}
        函数首先构造一个CATEGORY_LAUNCHER类型的Intent:

    final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);    mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
        接着从mContext变量中获得PackageManagerService的接口:

    final PackageManager packageManager = mContext.getPackageManager();

       下一步就是通过这个PackageManagerService.queryIntentActivities接口来取回所有Action类型为Intent.ACTION_MAIN,并且Category类型为Intent.CATEGORY_LAUNCHER的Activity了。

       我们先进入到PackageManagerService.queryIntentActivities函数中看看是如何获得这些Activity的,然后再回到这个函数中来看其余操作。

       Step 19. PackageManagerService.queryIntentActivities

       这个函数定义在frameworks/base/services/java/com/android/server/PackageManagerService.java文件中:

class PackageManagerService extends IPackageManager.Stub { ...... public List<ResolveInfo> queryIntentActivities(Intent intent,   String resolvedType, int flags) {  ......  synchronized (mPackages) {   String pkgName = intent.getPackage();   if (pkgName == null) {    return (List<ResolveInfo>)mActivities.queryIntent(intent,      resolvedType, flags);   }   ......  }  ...... } ......}

        回忆前面一篇文章Android应用程序安装过程源代码分析,系统在前面的Step 8中启动PackageManagerService时,会把系统中的应用程序都解析一遍,然后把解析得到的Activity都保存在mActivities变量中,这里通过这个mActivities变量的queryIntent函数返回符合条件intent的Activity,这里要返回的便是Action类型为Intent.ACTION_MAIN,并且Category类型为Intent.CATEGORY_LAUNCHER的Activity了。

        回到Step 18中的 LoaderTask.loadAllAppsByBatch函数中,从queryIntentActivities函数调用处返回所要求的Activity后,便调用函数tryGetCallbacks(oldCallbacks)得到一个返CallBack接口,这个接口是由Launcher类实现的,接着调用这个接口的.bindAllApplications函数来进一步操作。注意,这里又是通过消息来处理加载应用程序的操作的。

        Step 20. Launcher.bindAllApplications

        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/Launcher.java文件中:

public final class Launcher extends Activity  implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher { ...... private AllAppsView mAllAppsGrid; ...... public void bindAllApplications(ArrayList<ApplicationInfo> apps) {  mAllAppsGrid.setApps(apps); } ......}
        这里的mAllAppsGrid是一个AllAppsView类型的变量,它的实际类型一般就是AllApps2D了。

        Step 21. AllApps2D.setApps

        这个函数定义在packages/apps/Launcher2/src/com/android/launcher2/AllApps2D.java文件中:

public class AllApps2D extends RelativeLayout implements AllAppsView,  AdapterView.OnItemClickListener,  AdapterView.OnItemLongClickListener,  View.OnKeyListener,  DragSource { ...... public void setApps(ArrayList<ApplicationInfo> list) {  mAllAppsList.clear();  addApps(list); } public void addApps(ArrayList<ApplicationInfo> list) {  final int N = list.size();  for (int i=0; i<N; i++) {   final ApplicationInfo item = list.get(i);   int index = Collections.binarySearch(mAllAppsList, item,    LauncherModel.APP_NAME_COMPARATOR);   if (index < 0) {    index = -(index+1);   }   mAllAppsList.add(index, item);  }  mAppsAdapter.notifyDataSetChanged(); } ......}
        函数setApps首先清空mAllAppsList列表,然后调用addApps函数来为上一步得到的每一个应用程序创建一个ApplicationInfo实例了,有了这些ApplicationInfo实例之后,就可以在桌面上展示系统中所有的应用程序了。

        到了这里,系统默认的Home应用程序Launcher就把PackageManagerService中的应用程序加载进来了,当我们在屏幕上点击下面这个图标时,就会把刚才加载好的应用程序以图标的形式展示出来了:

        点击这个按钮时,便会响应Launcher.onClick函数:

public final class Launcher extends Activity  implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher { ...... public void onClick(View v) {  Object tag = v.getTag();  if (tag instanceof ShortcutInfo) {   ......  } else if (tag instanceof FolderInfo) {   ......  } else if (v == mHandleView) {   if (isAllAppsVisible()) {    ......   } else {    showAllApps(true);   }  } } ......}
        接着就会调用showAllApps函数显示应用程序图标:

public final class Launcher extends Activity  implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, AllAppsView.Watcher { ...... void showAllApps(boolean animated) {  mAllAppsGrid.zoom(1.0f, animated);  ((View) mAllAppsGrid).setFocusable(true);  ((View) mAllAppsGrid).requestFocus();  // TODO: fade these two too  mDeleteZone.setVisibility(View.GONE); } ......}
        这样我们就可以看到系统中的应用程序了:



        当点击上面的这些应用程序图标时,便会响应AllApps2D.onItemClick函数:

public class AllApps2D extends RelativeLayout implements AllAppsView,  AdapterView.OnItemClickListener,  AdapterView.OnItemLongClickListener,  View.OnKeyListener,  DragSource { ...... public void onItemClick(AdapterView parent, View v, int position, long id) {  ApplicationInfo app = (ApplicationInfo) parent.getItemAtPosition(position);  mLauncher.startActivitySafely(app.intent, app); } ......}

        这里的成员变量mLauncher的类型为Launcher,于是就调用Launcher.startActivitySafely函数来启动应用程序了,这个过程具体可以参考Android应用程序启动过程源代码分析一文。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow
这里写图片描述
你好! 这是你第一次使用 **Markdown编辑器** 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block var foo = 'bar'; 

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPEASCIIHTML
Single backticks'Isn't this fun?'‘Isn’t this fun?’
Quotes"Isn't this fun?"“Isn’t this fun?”
Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t &ThinSpace; . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

gantt
        dateFormat  YYYY-MM-DD
        title Adding GANTT diagram functionality to mermaid
        section 现有任务
        已完成               :done,    des1, 2014-01-06,2014-01-08
        进行中               :active,  des2, 2014-01-09, 3d
        计划一               :         des3, after des2, 5d
        计划二               :         des4, after des3, 5d
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图::

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件或者.html文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值