Android Service 详解

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

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

               

一个Service也是一种应用程序组件,它运行在后台以提供某种服务,通常不具有可见的用户界面。其它的应用程序组件可以启动一个Service,即使在用户切换到另外一个应用程序后,这个Service还是一直会在后台运行。此外,一个应用程序也可以绑定到一个Service然后使用进程间通信(IPC)方式与Service之间发生交互。例如一个Service可以处理网络事物,播放音乐,读写文件或者读写ContentProvider,所以这些都在后台运行。

   一个Service可以以以下两种形式存在:

·        Started(启动) 在一个应用程序以startService() 来启动一个Service时,这个Service将处于“Started”状态。一旦启动,这个Service可以在后台一直运行下去,即使启动它的应用程序已推出。通常,一个处于“started”状态的Service完成某个功能而不会给启动它的应用程序组件返回结果。比如,这个服务(Service)可能是上载或是下载一个文件,当任务完成后,服务自行退出。

·        Bound (绑定) 当一个应用程序组件以bindService() 绑定一个Service时,这个Service处于“Bound”状态。处于“Bound”状态的Service提供了一个客户/服务(C/S)调用接口支持其它应用程序组件和它交互,比如发生请求,返回结果,或者使用IPC完成跨进程间通信。一个处于“Bound”的Service只能和与其绑定的应用程序一起运行。多个应用程序组件可以绑定到同一个Service。当所有绑定的应用程序组件都退出绑定后,被“绑定”的Service将被销毁。

  对于一个Service来说,它可以是“Started”,“Bound”或者同时处于两种状态。其它任一应用程序组件(比如一个Activity)都可以使用这个Service,即使其它应用程序组件是在不同的应用程序中。当然你可以把Service定义为私有的,这样其它应用程序就无法使用你定义的Service。

  要注意的是,一个Service运行在其宿主进程的主线程中--服务不会自己创建新的线程也不运行在独立的进程中(除非你特别指明)。这意味着,如果你的Service需要完成一些很耗CPU资源的操作(比如播放MP3,或者使用网络),你应该在Service中创建新的线程来完成这些工作,从而降低出现程序无响应(ANR)的风险。

Service基础

   要创建一个Service,你必须从Service或是其某个子类派生子类。在你的Service子类实现中,你需要重载一些方法以支持Service重要的几个生命周期函数和支持其它应用组件绑定的方法。下面给出几个需要重载的重要方法:

·        onStartCommand()   Android系统在有其它应用程序组件使用startService()请求启动Service时调用。一旦这个方法被调用,Service处于“Started”状态并可以一直运行下去。如果你实现了这个方法,你需要在Service任务完成时调用stopSelf()或是stopService()来终止服务。如果你只支持“绑定”模式的服务,你可以不实现这个方法。

·        onBind()  Android系统中有其他应用程序组件使用bindService()来绑定你的服务时调用。在你实现这个方法时,你需要提供一个IBinder接口以支持客户端和服务之间通信。你必须实现这个方法,如果你不打算支持“绑定”,返回Null即可。

·        onCreate()  Android系统中创建Service实例时调用,一般在这里初始化一些只需单次设置的过程(在onStartCommand和onBind()之前调用),如果你的Service已在运行状态,这个方法不会被调用。

·        onDestroy() Android系统中Service不再需要,需要销毁前调用。在你的实现中你需要释放一些诸如线程,注册过的listener,receiver等,这是Service被调用的最后一个方法。

如果一个Service是由startService()启动的(这时 onStartCommand()将被调用),这个Service将一直运行直到调用stopSelf()或其它应用部件调用stopService()为止。

如果一个部件调用bindService()创建一个Service(此时onStartCommand()不会调用),这个Service运行的时间和绑定它的组件一样长。一旦其他组件解除绑定,系统将销毁这个Service。

Android系统只会在系统内存过低且不得不为当前活动的Activity恢复系统资源时才可能强制终止某个Service。如果这个Service绑定到一个活动的Activity,基本上不会被强制清除。如果一个Service被申明成“后台运行”,就几乎没有被销毁的可能。否则的话,如果Service启动后并长期运行,系统将随着时间的增加降低其在后台任务中的优先级,其被杀死的可能性越大。如过你的Service是做为“Started”状态运行,你必须设计后如果在系统重启服务时优雅退出。如果系统杀死你的服务,系统将在系统资源恢复正常时重启你的服务(当然这也取决于onStartCommand()的返回值)。

在Manifest中申明Service

    和Activity一样,你必须在Manifest文件中申明应用中使用到的Service。为了声明一个Service,你需要定义<application>的子元素<service>,比如:

<manifest ... >  ...  <application ... >      <service android:name=".ExampleService" />      ...  </application></manifest>


        <service>还包括一些其它属性,android:name是唯一一个必须定义的属性,它定义了Service的类名。

和Activity一样,你可以为Service定义Intent Filter以支持其它部件隐式调用该服务。如果你只打算在你自己的应用中使用某个Service,那么就不要定义任何Intent Filter。在这种情况下,你必须明确指明Service的类名来启动这个Service。此外,你可以通过设置android:exported属性为false来明确说明该Service为私有,即使你为Service定义了Intent Filter ,其它应用也无法使用你的Service。

 创建一个“Started”的Service

当其它应用程序组件使用startService()来启动Service后,这个Service就成为“Started”的Service。这时Service的onStartCommand () 回调函数将会被调用。

当一个Service以startService()启动后,处于“Started”状态,其生命周期独立于启动这个Service的其它应用程序组件,并且可以在后台无限期运行,即使启动它的其它应用程序组件已经退出。在这种情况下,Service可以调用stopSelf()或者其它程序部分调用stopService()来结束这个Service。

一个应用程序组件比如一个Activity可以通过传入Intent使用startService()来启动一个Service,这个Intent指明需要的服务并包含了Service需要的数据。Service在onStartCommand回调函数中可以访问这个Intent。

要注意的是,Service缺省和申明它的应用程序使用同一个进程并且运行在主线程中,因此如果你的Service比较耗时的话,那么这个Service会影响到应用的用户响应性能。为避免这种现象,你应用在Service中创建新线程。

你可以使用下面两个基类来创建一个“Started”的Service。

·        Service 这是所有Service的基类,当你派生这个类时,使用新创建的线程来完成Service需要完成的工作非常重要,这是因为Service缺省也会使用你的应用程序的主线程,这可能会影响你应用程序的响应性能。

·        IntentService 为Service的一个子类,它会使用一个工作线程来处理所有的请求,每次处理一个请求。这在你无需实现并行处理多个请求时是最好的选择。你只需重载onHandleIntent()方法,这个方法接受每个请求,从而你可以在后台完成所需任务。

 派生IntentService类

因为大部分“Started”的Service不需要并行处理多个请求,这时最好的选择是派生自IntentService。

类IntentService可以完成以下工作:

·        创建一个工作线程来处理所有发送到onStartCommand()的请求(Intent),从而和应用的主线程分开。

·        创建一个工作队列来逐个处理每个请求,从而无需考虑多线程编程。

·        在处理完所有请求后中止Service的运行,你无需调用stopSelf()来终止Service。

·        提供onBind()的缺省实现,返回null。

·        提供onStartCommand()的缺省实现,将接受到的Intent发送到工作队列中,然后调用你的onHanleIntent()实现。

因此,对于你来说,只需要提供onHandleIntent()来处理发过来的Intent,当然你可能需要实现Service的一个简单的构造函数。

下例为IntentService的一个简单实现:

public class HelloIntentService extends IntentService /**  * A constructor is required, and must   * call the super IntentService(String)  * constructor with a name for the worker thread.  */ public HelloIntentService() {  super("HelloIntentService"); } /**  * The IntentService calls this method from   * the default worker thread with  * the intent that started the service.   * When this method returns,  * IntentService stops the service, as appropriate.  */ @Override protected void onHandleIntent(Intent intent) {  // Normally we would do some work here, like download a file.  // For our sample, we just sleep for 5 seconds.  long endTime = System.currentTimeMillis() + 5 * 1000;  while (System.currentTimeMillis() < endTime) {   synchronized (this) {    try {     wait(endTime - System.currentTimeMillis());    } catch (Exception e) {    }   }  } }


你需要实现的只是一个构造函数和重载onHandleIntent()。

如果你需要重载其它一些回调函数,比如onCreate(),onStartCommand()或是onDestroy(),注意调用其基类的实现。这样IntentService才能正确管理工作线程的生命周期。

比如重载onStartCommand(),你必须返回基类的onStartCommand()的返回值,这样Intent能够传递到onHandleIntent()的原因:

@Overridepublic int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); return super.onStartCommand(intent,flags,startId);}


   除了onHandleIntent(),此外唯一可以不调用基类实现的回调函数为onBind(),你只要在你需要支持绑定服务时才需要实现该方法。

派生Service类

   从上面可以看到使用IntentService可以让实现“Started”的Service变的非常简单,然而如果你需要支持使用多线程并行处理多个请求(而非使用工作队列来依次处理请求),这时你就需要从Service类来派生。

   作为比较,下面的例子使用Service实现和上面使用IntentService同样的功能,也是使用一个工作线程来逐个处理每个请求,可以看成是IntentService的一个实现:

public class HelloService extends Service private Looper mServiceLooper;  private ServiceHandler mServiceHandler;  // Handler that receives messages from the thread  private final class ServiceHandler extends Handler {      public ServiceHandler(Looper looper) {          super(looper);      }      @Override      public void handleMessage(Message msg) {          // Normally we would do some work here, like download a file.          // For our sample, we just sleep for 5 seconds.          long endTime = System.currentTimeMillis() + 5*1000;          while (System.currentTimeMillis() < endTime) {              synchronized (this) {                  try {                      wait(endTime - System.currentTimeMillis());                  } catch (Exception e) {                  }              }          }          // Stop the service using the startId, so that we don't stop          // the service in the middle of handling another job          stopSelf(msg.arg1);      }  }  @Override  public void onCreate() {    // Start up the thread running the service.  Note that we create a    // separate thread because the service normally runs in the process's    // main thread, which we don't want to block.  We also make it    // background priority so CPU-intensive work will not disrupt our UI.    HandlerThread thread = new HandlerThread("ServiceStartArguments",            Process.THREAD_PRIORITY_BACKGROUND);    thread.start();        // Get the HandlerThread's Looper and use it for our Handler     mServiceLooper = thread.getLooper();    mServiceHandler = new ServiceHandler(mServiceLooper);  }  @Override  public int onStartCommand(Intent intent, int flags, int startId) {      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();      // For each start request, send a message to start a job and deliver the      // start ID so we know which request we're stopping when we finish the job      Message msg = mServiceHandler.obtainMessage();      msg.arg1 = startId;      mServiceHandler.sendMessage(msg);            // If we get killed, after returning from here, restart      return START_STICKY;  }  @Override  public IBinder onBind(Intent intent) {      // We don't provide binding, so return null      return null;  }    @Override  public void onDestroy() {    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();   }}


    可以看出来,比使用IntentService多了很多工作。然而,由于你自己来处理onStartCommand(),你可以实现并行处理多个请求。当然这不是本例的实现,但是如果你愿意,你可以为每个请求创建一个工作线程来立即处理请求而不使用工作队列。

注意onStartCommand()方法必须返回一个整数,这个返回值定义了如果Android系统在杀死这个Service后又需要使用这个Service后的行为。这个返回值可以有下面几种情况:

·        START_NOT_STICKY 如果Android系统在onStartCommand() 返回后杀死了这个Service,系统不会重新创建这个Service,除非它是一个Pending Intent。这是在你的应支持简单的重启没有完成的任务避免无必要的Service运行时ic最好的选项。

·        START_STICKY 在Android系统在onStartCommand () 返回后,选择重新创建这个Service并调用Service的onStartCommand () 方法,但不会重新发送上一次的请求。Android系统使用空Intent调用onStartCommand,除非它是一个PendingIntent。这种情况适用于一些媒体播放器服务。

·        START_REDELIVER_INTENT在Android系统在onStartCommand() 返回后,选择重新创建这个Service并调用Service的onStartCommand () 方法,并且重新发送上一次的请求。这适用一些比较活跃地进行某些工作的服务,需要立即恢复活动比如下载文件的服务。

 启动Service

   在Activity或是其他应用程序组件可以通过传递Intent调用startService()方法来启动一个Service。Android系统然后会调用Service的onStartCommand()方法并传入这个Intent对象。

   比如,一个Activity可以通过明确指明调用的服务来调用前面创建的HelloService:

Intent intent = new Intent(this, HelloService.class);startService(intent);


    startService() 调用后立即返回,Android系统调用Service的onStartCommand(),如果这个Service之前没有运行,系统还将先调用Service的onCreate()方法,然后再调用onStartCommand()。

   如果Service没有提供绑定支持,那么使用startService()是唯一可以启动Service的方法。然而,如果你需要Service返回一个结果,那么客户端可以通过创建一个用于广播的延迟Intent(PendingIntent)然后通过Intent发送请求到Service。Service可以利用这个广播(Broadcast)返回结果。

   多个启动Service请求将导致多次调用Service的onStartCommand()方法。然而只要一个stopSelf或stopService来停止一个Service的运行。

 停止Service

一个“Started”的Service必须管理自己的生命周期,也就是说,除非系统资源不足,Android系统不终止或停止一个Service。因此Service必须调用stopSelf () 方法来终止自身的运行,或者其它部件可以通过stopService () 方法来中止一个Service的运行。

一旦Service被请求终止,系统将尽快销毁这个Service。

但是,如果你的Service需要在onStartCommand()并行处理多个请求,那么你不应再处理完一个请求后就停止Service的运行,因为你可能还会接受到新的请求(在第一个请求处理完就结束则会终止第二个请求的执行)。为避免这种情况,你可以使用带参数的stopSelf(int) 来确保终止Service的请求总是对应于最近的请求。也就是说,在调用stopSelf(int) 时,你通过传入请求的Id (传给onStartCommand 方法的 startId )来终止对应的请求处理。那么如果Service在调用stopSelf (int) 前接受到一个新的请求,那么Id将不会匹配,因此Service也就不会结束。

创建一个支持“绑定”的Service

一个支持“绑定”的Service是指运行其它应用程序之间通过调用bindService() 来绑定到它的Service。支持“绑定”的Service在设计时通常不允许直接使用startService() 来启动它。

在Activity或其它组件需要和一个Service发生交互时,你应该创建一个“绑定”的Service,在这个Service中你可以通过进程间通信接口(IPC)来提供可供其它应用程序组件使用的功能接口。

为了创建一个支持“绑定”的Service,你需要实现onBind() 回调函数并返回一个IBind接口对象,这个对象提供了其它部分可以访问这个Service的服务接口。其它应用程序组件然后可以通过bindService () 方法获得Service的服务接口对象,然后使用这个对象的方法来调用服务。通常“绑定”的Service的运行周期和绑定它的其它应用程序组件的生命周期是一样的。因此如果这个Service不再有应用程序组件与之绑定,Android系统会自动销毁这个Service,而你自己无需停止这个Service。

为了创建一个支持“绑定”的Service所需做的第一件事是定义可供客户端调用的服务接口。这个接口必须为一个IBinder接口,并且你的Service必须通过onBind () 返回这个接口对象。一旦客户端获得这个IBinder接口,就可以通过这个接口对象来使用服务。

多个客户端可以同时绑定到同一个Service,当一个客服端使用好所需的服务后,调用onbindService() 来解除与Service之间的绑定关系。当一个Service没有和它绑定的客户端后,Android系统销毁这个Service。

给用户发送通知

   Service通常是在后台运行,它可以通过Toast 通知或是状态条通知来提醒用户发生了某种事件。

   一个Toast通知为覆盖在当前屏幕上短时间显示的消息,而状态条通知可以在状态条以图标和文字的方法显示,用户可以选择是否查阅这个通知并作出处理(比如启动一个Activity)。

   一般来说,使用状态条通知是在某个后台工作完成后(比如文件下载完毕)通知用户的最好方法。用户可以在扩展后的通知窗口选择某个通知,然后可以点击是否启动一个Activity(比如查看下载好的文件)

在前台运行Service

通常Service在后台运行,但有时某些Service是用户明确知道在运行,即使在系统资源不足时,也不该做为销毁的候选Service,这种Service可以设置成在前台运行,在前台运行的Service必须在状态条显示通知,除非Service停止或者转入后台,这种通知不可以被忽视。

比如,如果一个音乐播放器使用Service来播放,这个Service应该被放在前台运行,因为用户很清楚的知道音乐在播放,而在状态条显示的通知理当显示当前播放的乐曲并且允许用户启动一个Activity来操作这个音乐播放器。

要把一个Service放在前台运行,可以调用startForeground()。这个方法接受两个参数:一个整数来唯一标识一个通知,和显示在状态条上的通知对象。

例如:

要使一个Service推出前台运行,调用stopForeground(). 这个方法接受一个boolean参数,指明是否同时移除状态条上的通知。 这个方法并不会停止Service的运行。但如果你停止一个在前台运行的Service,那么会同时移除状态条的通知。

管理Service的生命周期

和Activity相比,Service的生命周期要简单很多。但你更要关注你的Service是如何创建和销毁的,这是因为Service可以在用户不知道的情况下在后台运行。

Service的生命周期,从其创建后到销毁,可以有两条不同的路径:

“Started”的Service 这个Service是由其它的应用程序组件调用startService()创建的,这个service 可以在后台无限期运行直到调用 stopSelf()或者其它组件调用stopService()来停止它。当Service停止时,系统将销毁这个Service。

支持“绑定”的Service 这种Service是在其它组件调用 bindService()绑定它时创建,客户端然后可以通过服务接口和Serivce通信。客户端可以调用 unbindService () 解除与Service的绑定。多个客户端可以同时绑定同一Service,当一个Service没有客户端和它绑定时,系统则销毁这个Service。

这两条路径并不是完全分开的,也就是说,你可以去绑定一个已经是“Started”状态的Service。比如,一个通过媒体播放的Service可能通过指明需要播放音乐的Intent然后调用startService() 启动的。再往后,用户可能打算来操作媒体播放器,那么一个Activity可以调用 bindServive()来绑定这个Service。在这种情况下,stopSelf()或stopSelf()并不真正停止Service直到所有的客户端都解除与Service的绑定。

实现生命周期回调函数

和Activity类似,Service也提供了一些回调函数接口允许你重载这些方法来监视Service状态的变化并添加合适的处理,下面代码给出一个Service生命周期回调函数实现的框架:

package com.example.bookdemo;import android.app.Service;import android.content.Intent;import android.os.IBinder;public class ExampleService extends Service // indicates how to behave if the service is killed    int mStartMode;      // interface for clients that bind    IBinder mBinder;         // indicates whether onRebind should be used    boolean mAllowRebind;     @Override    public void onCreate() {        // The service is being created    }    @Override    public int onStartCommand(Intent intent,       int flags, int startId) {        // The service is starting,      // due to a call to startService()        return mStartMode;    }    @Override    public IBinder onBind(Intent intent) {        // A client is binding to the service      // with bindService()        return mBinder;    }    @Override    public boolean onUnbind(Intent intent) {        // All clients have unbound with unbindService()        return mAllowRebind;    }    @Override    public void onRebind(Intent intent) {        // A client is binding to the service with bindService(),        // after onUnbind() has already been called    }    @Override    public void onDestroy() {        // The service is no longer used and is being destroyed    }}


  和Activty不同的是,Service的生命周期回调函数没有必要调用基类的对于的方法。

 

图 1.                     Servcie的生命周期

通过实现这些回调函数,你可以监视Service生命周期中两个嵌套的循环:

·        整体生命周期 指Service在onCreate()和onDestroy()之间。和Activity类似,Service可以在onCreate () 进行一些初始化工作,而在onDestroy () 中释放资源。比如,一个音乐播放器可以在onCreate() 创建用来播放音乐的线程,而在onDestroy() 停止这个线程。

·        活动生命周期 Service 在 onStartCommand() 或 onBind() 后开始活动,每个方法分别处理来自 startService和 bindService() 发过来请求 Intent。如果是“Started”的Service,那么它活动的生命周期和Service的整个生命周期是一致的。如果是“绑定”的Service,那么它的活动生命周期终止与unbind()。

 

           

给我老师的人工智能教程打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、付费专栏及课程。

余额充值