一直用的android手机,用过这么多的app,平时也会遇到有趣的通知提醒,在这里先总结两种吧,notification和图标数字,有的以后看到再研究。还有,推广一下哈,刚刚建立一个Q群544645972,有兴趣的加一下,一起成长。

Notification

  Notification应该算是最常见的app通知方式了,网上资料也很多,各种使用方法官方文档也已经写的非常详细了:http://developer.android.com/intl/zh-cn/guide/topics/ui/notifiers/notifications.html。这里就介绍一下几种特殊的用法:

动态改变

  将一个notification的setOngoing属性设置为true之后,notification就能够一直停留在系统的通知栏直到cancel或者应用退出。所以有的时候需要实时去根据情景动态改变notification,这里以一个定时器的功能为例,需要每隔1s去更新一下notification,具体效果:
  
  非常简单的功能,代码也很简单:

private Timer timer;
private TimerTask task;
...
if (timer != null)return;
timer = new Timer("time");
task = new TimerTask() {@Overridepublic void run() {showDynamicNotification();}
};
timer.scheduleAtFixedRate(task, 0, 1000);private void showDynamicNotification() {L.i("show dynamic notification");mBuilder = new NotificationCompat.Builder(NotificationActivity.this);RemoteViews view = new RemoteViews(getPackageName(), R.layout.layout_notification);view.setTextViewText(R.id.tv_number, parseDate());view.setImageViewResource(R.id.iv_icon, R.mipmap.ic_launcher);Intent intent = new Intent(NOTIFY_ACTION);PendingIntent pendingIntent = PendingIntent.getBroadcast(NotificationActivity.this,1000, intent, PendingIntent.FLAG_UPDATE_CURRENT);mBuilder.setSmallIcon(R.mipmap.ic_launcher).setContentIntent(pendingIntent).setTicker("you got a new message").setOngoing(true).setContent(view);notification = mBuilder.build();notificationManager.notify(NOTIFY_ID2, notification);
}private String parseDate() {SimpleDateFormat format = new SimpleDateFormat("yyyy hh:mm:ss", Locale.getDefault());return format.format(System.currentTimeMillis());
}

  需要注意的是Notification.Builder 是 Android 3.0 (API 11) 引入的,为了兼容低版本,我们一般使用 Support V4 包提供的 NotificationCompat.Builder 来构建 Notification。要想动态更新notification,需要利用 NotificationManager.notify() 的 id 参数,该 id 在应用内需要唯一(如果不唯一,在有些4.x的手机上会出现pendingIntent无法响应的问题,在红米手机上出现过类似情况),要想更新特定 id 的通知,只需要创建新的 notification,并触发与之前所用 id 相同的 notification,如果之前的通知仍然可见,则系统会根据新notification 对象的内容更新该通知,相反,如果之前的通知已被清除,系统则会创建一个新通知。
  在这个例子中使用的是完全自定义的remoteViews,remoteViews和普通view的更新机制不一样,网上资料很多,感兴趣的可以去仔细了解。还有一个就是PendingIntent,这就不详细介绍了,这里简单列一下PendingIntent的4个flag的作用

  • FLAG_CANCEL_CURRENT:如果构建的PendingIntent已经存在,则取消前一个,重新构建一个。
  • FLAG_NO_CREATE:如果前一个PendingIntent已经不存在了,将不再构建它。
  • FLAG_ONE_SHOT:表明这里构建的PendingIntent只能使用一次。
  • FLAG_UPDATE_CURRENT:如果构建的PendingIntent已经存在,则替换它,常用。

big view

  Notification有两种视觉风格,一种是标准视图(Normal view)、一种是大视图(Big view)。标准视图在Android中各版本是通用的,但是对于大视图而言,仅支持Android4.1+的版本,比如邮件,音乐等软件就会使用到这种大视图样式的扩展通知栏,系统提供了setStyle()函数用来设置大视图模式,一般情况下有三种模式提供选择:

  • NotificationCompat.BigPictureStyle, 在细节部分显示一个256dp高度的位图
  • NotificationCompat.BigTextStyle,在细节部分显示一个大的文本块。
  • NotificationCompat.InboxStyle,在细节部分显示一段行文本。
      
    在21版本之后增加了一个Notification.MediaStyle,这个可以达到类似
      
    的效果,基本和一些主流媒体播放器的界面类似了,在这就不具体介绍了,提供一篇资料:controllingMedia
      如果不使用上面的几种style,完全自定义布局也是可以的,例如实现:
      
    相关源码:Android custom notification for music player Example。
      在这我就以一个简单的实现为例,效果如下:
      
    代码:
RemoteViews smallView = new RemoteViews(getPackageName(), R.layout.layout_notification)
smallView.setTextViewText(R.id.tv_number, parseDate())
smallView.setImageViewResource(R.id.iv_icon, R.mipmap.ic_launcher)mBuilder = new NotificationCompat.Builder(NotificationActivity.this)
mBuilder.setSmallIcon(R.mipmap.ic_launcher).setNumber((int) (Math.random() * 1000))//No longer displayed in the status bar as of API 21..setTicker().setDefaults(Notification.DEFAULT_SOUND| Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS)
//        .setDeleteIntent().setAutoCancel(true).setWhen(0).setPriority(NotificationCompat.PRIORITY_LOW)intent = new Intent(NOTIFY_ACTION)
pendingIntent = PendingIntent.getBroadcast(NotificationActivity.this,1000, intent, PendingIntent.FLAG_UPDATE_CURRENT)
mBuilder.setContentIntent(pendingIntent)//在5.0版本之后,可以支持在锁屏界面显示notification
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){mBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
}
notification = mBuilder.build()
notification.contentView = smallView//如果系统版本 >= Android 4.1,设置大视图 RemoteViews
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {RemoteViews view = new RemoteViews(getPackageName(), R.layout.layout_big_notification)view.setTextViewText(R.id.tv_name, )view.setOnClickPendingIntent(R.id.btn_click_close,PendingIntent.getBroadcast(NotificationActivity.this, 1001,new Intent(CLICK_ACTION), PendingIntent.FLAG_UPDATE_CURRENT))//textview marquee property is useless for bigContentViewnotification.bigContentView = view
}notificationManager.notify(NOTIFY_ID3, notification)

xml布局:

<linearlayout android:background="#ef222222" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="horizontal" android:padding="10dp" xmlns:android="http://schemas.android.com/apk/res/android"><framelayout android:layout_height="150dp" android:layout_width="match_parent"><imageview android:background="@mipmap/ic_launcher" android:id="@+id/iv_icon" android:layout_gravity="center_vertical" android:layout_height="wrap_content" android:layout_width="wrap_content"><linearlayout android:layout_height="match_parent" android:layout_weight="1" android:layout_width="match_parent" android:orientation="vertical"><textview android:ellipsize="marquee" android:fadingedge="horizontal" android:focusable="true" android:focusableintouchmode="true" android:gravity="center_horizontal|center_vertical" android:id="@+id/tv_name" android:layout_gravity="center" android:layout_height="wrap_content" android:layout_width="fill_parent" android:marqueerepeatlimit="marquee_forever" android:scrollhorizontally="false" android:singleline="true" android:textcolor="#fff" android:textsize="15sp" android:textstyle="bold"><requestfocus></requestfocus></textview><button android:background="#ef222222" android:id="@+id/btn_click_close" android:layout_gravity="center_horizontal" android:layout_height="40dp" android:layout_margintop="10dp" android:layout_width="40dp" android:text="X" android:textsize="30sp" type="submit"></button></linearlayout></framelayout></imageview></linearlayout>

  这里有几点需要着重说明一下

  • setTicker函数在21版本之后已经Deprecated了,没有效果。
  • 监听notification删除函数setDeleteIntent是在11版本后新增的,而且setAutoCancel为true后,该函数会失效。
  • setPriority函数用来给notification设置优先级,上面给的google文档中有很详细的介绍。
  • 21版本之后,可以支持在锁屏界面显示notification,这个在google文档中也有介绍,这个体验对于我个人来说感触很深,对于短信等私密性通知可以隐藏,但是对于一般毫无隐私的应用通知,就可以设置其为public,省去用户解锁,下拉通知栏的操作。
  • 自定义大图模式也是将自定义的RemoteViews赋值给notification.bigContentView变量,而且这个功能也只是在api16(4.1)之后生效。
  • 大图模式高度的设置有些奇怪,在上面的xml文件中,LinearLayout设置高度是无效的,必须要套一层FrameLayout,设置FrameLayout的高度才行,貌似定义最外层的LinearLayout的layoutParams是无效的。
  • 在bigContentView中是无法实现textview的marquee效果,而且事实也很奇怪,单独使用contentView,传入的remoteViews中的textview的marquee属性是好用的,但是一旦设置了bigContentView,contentView中的textview属性也失效了,这点使用的时候要注意。

浮动通知

  这种效果大家应该在微信中看的很多,其实实现也很简单:
  
代码:

RemoteViews headsUpView = new RemoteViews(getPackageName(), R.layout.layout_heads_up_notification)intent = new Intent(NOTIFY_ACTION)
pendingIntent = PendingIntent.getBroadcast(NotificationActivity.this,1000, intent, PendingIntent.FLAG_UPDATE_CURRENT)
mBuilder = new NotificationCompat.Builder(NotificationActivity.this)
mBuilder.setSmallIcon(R.mipmap.ic_launcher).setContentTitle().setContentText().setNumber((int) (Math.random() * 1000)).setTicker()//must set pendingintent for this notification, or will be crash.setContentIntent(pendingIntent).setDefaults(Notification.DEFAULT_SOUND| Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS).setAutoCancel(true).setWhen(0)
notification = mBuilder.build()
if (Build.VERSION.SDK_INT >= 21) {notification.priority = Notification.PRIORITY_MAXnotification.headsUpContentView = headsUpView
}
notificationManager.notify(NOTIFY_ID1, notification)

  这个效果非常的方便,用户都不需要直接下拉出通知栏,直接就能够看见,省去了多余操作,google官方文档介绍:http://developer.android.com/intl/zh-cn/guide/topics/ui/notifiers/notifications.html#Heads-up。headsUpContentView属性也只是在21版本时出现,使用的时候需要注意。

notification常见问题总结

  1.通过notification打开activity的时候,就要涉及到保存用户导航的问题,这个时候就要使用到activity task的相关内容了,我以前写过一篇博客中有介绍到activity task的内容:android深入解析Activity的launchMode启动模式,Intent Flag,taskAffinity,感兴趣的可以去看看。那么要实现点击notification打开指定activity,就需要设置相关的pendingIntent,有两种特殊的情况需要说明一下:  

  • 第一种是需要打开该activity的整个task栈,也就是说父activity也需要同时全部打开,而且按照次序排列在task栈中。 但是这里会有一个问题,它在打开整个activity栈之前会先清空原先的activity task栈,所以最后在task栈中只剩下相关的几个activity,举个例子我要打开A->B->C的activity栈,但是我原先的activity栈中有D和C这两个activity,系统会直接按顺序关闭D和C这两个activity,接着按顺序打开A->B->C,这种情况在使用的时候需要注意。
  • 第二种是直接打开一个activity在一个单独的task栈中 这种情况会生成两个task栈, 这两种情况在google官方文档中已经详细介绍了:http://developer.android.com/intl/zh-cn/guide/topics/ui/notifiers/notifications.html#NotificationResponse。

  2.我在以前的博客中曾经介绍过一个notification图标变成白块的问题:android5.0状态栏图标变成白色,这个问题在国产的很多rom中自己解决了,例如小米,锤子等,但是例如HTC和三星等rom仍然是有这样的问题,要重视起来啊
  3.列表内容在2.3的时候直接使用

RemoteViews rvMain = new RemoteViews(context.getPackageName(), R.layout.notification_layout);
//TODO rvMain...
NotificationCompat.Builder builder = new NotificationCompat.Builder(context).setContent(rvMain);
// TOOD ...

  是无效的,需要换一种方式:

RemoteViews rvMain = new RemoteViews(context.getPackageName(), R.layout.notification_layout);
//TODO rmMain...
NotificationCompat.Builder builder = new NotificationCompat.Builder(context).setContent(rvMain);
// TOOD ...
Notification notification = builder.build();
if(Build.VERSION.SDK_INT <= 10){notification.contentView = rvMain;
}

  4.通知栏上的操作事件:

  setContentIntent():用户点击通知时触发
  setFullScreenIntent()://TODO 这个在通知显示的时候会被调用
  setDeleteIntent():用户清除通知时触发,可以是点击清除按钮,也可以是左右滑动删除(当然了,前提是高版本)
  2.3及以下是无法处理自定义布局中的操作事件的,这样我们就不要去考虑增加自定义按钮了。

notification源码解析

  分析一下源码,以NotificationManager.notify为入口进行分析:

INotificationManager service = getService()
......
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,stripped, idOut, UserHandle.myUserId())

getService()函数:

static public INotificationManager getService()
{if (sService != null) {return sService;}IBinder b = ServiceManager.getService();sService = INotificationManager.Stub.asInterface(b);return sService;
}

该函数通过ServiceManager.getService(“notification”)获取了INotificationManager的Binder对象,用来进行跨进程通信,Binder不太明白的可以看看我以前写的一篇博客:android IPC通信(下)-AIDL。这里获取的Binder对象就是NotificationManagerService,这里涉及的两个类要介绍一下,StatusBarManagerService和NotificationManagerService,这两个service都会在frameworks/base/services/java/com/android/server/SystemServer.java文件里面进行启动的:

class ServerThread extends Thread {
public void run() {
......  StatusBarManagerService statusBar = null;  NotificationManagerService notification = null;
......  statusBar = new StatusBarManagerService(context, wm);  ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
......  notification = new NotificationManagerService(context, statusBar, lights);                     ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification);
......  }  } 

我在早期的博客中介绍过SystemServer,system_server子进程是zygote通过forkSystemServer函数创建的,感兴趣的可以去看看android启动过程详细讲解。上面的代码就调用到了NotificationManagerService的enqueueNotificationWithTag方法,enqueueNotificationWithTag方法会调用到enqueueNotificationInternal方法,这个方法就是核心了,我们抽取其中比较重要的代码分析一下:

void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,final int callingPid, final String tag, final int id, final Notification notification,int[] idOut, int incomingUserId) {...//------这里会做一个限制,除了系统级别的应用之外,其他应用的notification数量会做限制,//------用来放置DOS攻击导致的泄露// Limit the number of notifications that any given package except the android// package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks....//------post到工作handler中进行工作mHandler.post(new Runnable() {@Overridepublic void run() {synchronized (mNotificationList) {// === Scoring ===//------审查参数priority// 0. Sanitize inputsnotification.priority = clamp(notification.priority, Notification.PRIORITY_MIN,Notification.PRIORITY_MAX);.....//------初始化score// 1. initial score: buckets of 10, around the app [-20..20]final int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER;//------将前面传递进来的Notification封装成一个StatusBarNotification对象,然后//------和score封装成一个NotificationRecord对象,接着会调用handleGroupedNotificationLocked//------方法,看能否跳过下一步操作,额外的会对downloadManager进行单独处理// 2. extract ranking signals from the notification data.....//------主要是统计notification的各种行为,另外将该上面封装好的NotificationRecord对象//------加入到mNotificationList中,然后排序,排序外后,如果notification设置了smallIcon,//------调用所有NotificationListeners的notifyPostedLocked方法,通知有新的notification,//------传入的参数为上面封装成的StatusBarNotification对象。// 3. Apply local rules.....mRankingHelper.sort(mNotificationList);if (notification.getSmallIcon() != null) {StatusBarNotification oldSbn = (old != null) ? old.sbn : null;mListeners.notifyPostedLocked(n, oldSbn);} else {......}//通知status bar显示该notificationbuzzBeepBlinkLocked(r);}}});
}

notifyPostedLocked方法中会继续post到工作handler中,在该工作handler中调用notifyPosted方法,notifyPosted方法很简单,也是通过Binder调用到了NotificationListenerService中,这个NotificationListenerService中类很实用,它可以继承,用来监听系统notification的各种动作:Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解。通知完成,最后异步操作就是调用buzzBeepBlinkLocked()方法去显示该notification了,这个函数也很长,但是职责很明确,确认是否需要声音,震动和闪光,如果需要,那么就发出声音,震动和闪光:

private void buzzBeepBlinkLocked(NotificationRecord record) {.....// Should this notification make noise, vibe, or use the LED?......// If we're not supposed to beep, vibrate, etc. then don't......if (disableEffects == null&& (!(record.isUpdate&& (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))&& (record.getUserId() == UserHandle.USER_ALL ||record.getUserId() == currentUser ||mUserProfiles.isCurrentProfile(record.getUserId()))&& canInterrupt&& mSystemReady&& mAudioManager != null) {if (DBG) Slog.v(TAG, "Interrupting!");sendAccessibilityEvent(notification, record.sbn.getPackageName());// sound// should we use the default notification sound? (indicated either by// DEFAULT_SOUND or because notification.sound is pointing at// Settings.System.NOTIFICATION_SOUND).....// vibrate// Does the notification want to specify its own vibration?....// light....if (buzz || beep || blink) {EventLogTags.writeNotificationAlert(record.getKey(),buzz ? 1 : 0, beep ? 1 : 0, blink ? 1 : 0);mHandler.post(mBuzzBeepBlinked);}
}

最后将mBuzzBeepBlinked post到工作handler,最后会调用到mStatusBar.buzzBeepBlinked(),mStatusBar是StatusBarManagerInternal对象,这个对象是在StatusBarManagerService中初始化,所以最后调用到了StatusBarManagerService中StatusBarManagerInternal的buzzBeepBlinked()方法:

public void buzzBeepBlinked() {if (mBar != null) {try {mBar.buzzBeepBlinked();} catch (RemoteException ex) {}}
}

mBar是一个IStatusBar对象,这个mBar在哪里赋值的呢?看这里:http://www.programering.com/a/MTOzITNwATg.html,英文看不懂没关系,有中文版:http://home.bdqn.cn/thread-42153-1-1.html。所以最终调用到了CommandQueue类中,接着sendEmptyMessage给了内部的H类(貌似很喜欢用H这个单词作为Handler的命名,比如acitivity的启动:android 不能在子线程中更新ui的讨论和分析),接着调用了mCallbacks.buzzBeepBlinked()方法,这个mCallbacks就是BaseStatusBar,最终会将notification绘制出来,到这里一个notification就算是完成了。
  注:我分析代码的时候看的代码是最新版本的api 23代码,buzzBeepBlinked()这个函数在BaseStatusBar类中是不存在的,绘制代码是在UpdateNotification()函数中,但是BaseStatusBar分明是继承了CommandQueue.Callbacks接口,却没有实现它,所以这个buzzBeepBlinked()函数到最后就莫名其妙失踪了,求大神指点,非常疑惑。

相关资料

http://www.tutorialsface.com/2015/08/android-custom-notification-tutorial/
http://developer.android.com/intl/zh-cn/guide/topics/ui/notifiers/notifications.html#Heads-up
http://glgjing.github.io/blog/2015/11/18/android-kai-fa-zhi-notification-xiang-jie/
http://www.codeceo.com/article/android-notification-4-types.html
http://www.itnose.net/detail/6169442.html
http://www.cnblogs.com/over140/p/4249503.html
http://blog.csdn.net/loongggdroid/article/details/17616509/
http://www.2cto.com/kf/201408/327782.html
http://blog.csdn.net/xxbs2003/article/details/19167331
http://www.jianshu.com/p/4d76b2bc8784
http://home.bdqn.cn/thread-42153-1-1.html

图标数字

  
  虽然说这是iOS上的风格,但是在某些手机上还是支持的,比如三星和HTC(m8t,6.0)的有些手机都可以,小米手机是个特例,它是根据notification的数量来自动生成的。
  一般情况下,HTC和三星可以使用下面的函数生成

public static void setBadge(Context context, int count) {String launcherClassName = getLauncherClassName(context);if (launcherClassName == null) {return;}Intent intent = new Intent();intent.putExtra(, count);intent.putExtra(, context.getPackageName());intent.putExtra(, launcherClassName);context.sendBroadcast(intent);
}public static String getLauncherClassName(Context context) {PackageManager pm = context.getPackageManager();Intent intent = new Intent(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_LAUNCHER);List resolveInfos = pm.queryIntentActivities(intent, 0);for (ResolveInfo resolveInfo : resolveInfos) {String pkgName = resolveInfo.activityInfo.applicationInfo.packageName;if (pkgName.equalsIgnoreCase(context.getPackageName())) {String className = resolveInfo.activityInfo.name;return className;}}return null;
}

  由于android碎片化太严重,所以在不同手机上适配起来是非常麻烦,不过还好在github上国人写了一个库可以覆盖挺多机型:ShortcutBadger,也可以参考一下:http://stackoverflow.com/questions/17565307/how-to-display-count-of-notifications-in-app-launcher-icon。

源码

https://github.com/zhaozepeng/notification

android 特殊用户通知用法汇总--Notification源码分析相关推荐

  1. Android 9 (P)之init进程启动源码分析指南之三

          Android 9 (P)之init进程启动源码分析指南之三 Android 9 (P)系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 An ...

  2. Android 8.1/9.0 MTK Camera源码分析之快门声音控制流程

    Android 8.1/9.0 MTK Camera源码分析之快门声音控制 在Android 8.1上mtk camera有控制快门声音的接口,但是并没有了控制录像快门声音的接口.之所以会有这个现象, ...

  3. Android 9(P)之init进程启动源码分析指南之一

         Android 9 之init进程启动源码分析指南之一 Android 9 (P) 系统启动及进程创建源码分析目录: Android 9 (P)之init进程启动源码分析指南之一 Andro ...

  4. Android 8.1/9.0 MTK Camera源码分析之录像快门声音控制流程

    前面已经针对拍照快门声音控制流程进行了分析,接下来分析一下录像快门声音的控制流程. Android 8.1/9.0 MTK Camera源码分析之快门声音控制流程 这两篇文章其实都是相对于手机系统RO ...

  5. 【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 源码分析 | 创建 Protobuf 对象 )

    文章目录 一.Protobuf 源码分析 二.创建 Protobuf 对象 三.完整代码示例 四.参考资料 一.Protobuf 源码分析 Protobuf 源文件如下 : addressbook.p ...

  6. 【C++】Android (Light)RefBase-sp-wp引用计数-智能指针源码分析

    文章目录 1.RefBase简介 2.RefBase源码分析 3.RefBase使用注意事项 4.总结 1.RefBase简介 什么是RefBase?RefBase是Android中的一个C++类,用 ...

  7. 【Android CameraX】CameraXBasic —— 官方CameraX实例源码分析

    一.简介 二.源码分析 2.1 build.gradle 2.2 代码结构 2.3 变量 2.3.1 lensFacing 2.3.2 preview 2.3.3 Image capture 2.3. ...

  8. 解密android日志xlog,XLog 详解及源码分析

    一.前言 这里的 XLog 不是微信 Mars 里面的 xLog,而是elvishew的xLog.感兴趣的同学可以看看作者 elvishwe 的官文史上最强的 Android 日志库 XLog.这里先 ...

  9. Android SQLite多线程读写和线程同步源码分析

    没啥诀窍,只需保证几个线程都是用的一个SQLiteDataBase对象就行了. 如果我们非要在不同线程中用两个或更多的SQLiteDataBase对象呢,当然这些SQLiteDataBase对象所操作 ...

最新文章

  1. 亚马逊ses如何发qq_使用Amazon SES发送电子邮件
  2. 分享ASP.NET+jQuery MiniUI后台购物管理
  3. ABAP:SAP报表性能的优化
  4. Python 定时任务的实现方式
  5. c++ 显示图片_飞利浦256P1FR显示器一线直连MacBook使用体验分享
  6. Linux多进程拷贝文件
  7. lazy-mock ,一个生成后端模拟数据的懒人工具
  8. 微信公众号服务器推送307,关于公众号模板消息推送结果
  9. python怎么安装requests模块_python安装requests第三方模块
  10. Drools workbench kie-server部署和简单使用(全流程
  11. MedPy医学图像评价的python库
  12. HTML添加背景音乐代码
  13. 软件设计师中级考试,软考
  14. Win系统 - N卡如何组建三联屏?
  15. 用友杨雨春 融合是企业互联网化的下一个主旋律
  16. 项目管理: Alpha,Beta,RC,GA,Release
  17. 计算机科学的影响因子,影响因子最高的计算机科学期刊(前50种).doc
  18. Spring5框架day01之IOC容器
  19. 个人用户永久免费,可自动升级版Excel插件,使用VSTO开发,Excel催化剂功能第12波-快速生成、读取、导出条形码二维码...
  20. 秉承极简美学设计:Polestar 2 登陆中国后会威胁到谁?...

热门文章

  1. 奥克斯变频空调的控温温度波动初步研究
  2. activiti 定时事件
  3. 子网划分例题-等长划分、不等长划分
  4. matlab模拟短波天波,基于短波信道模型的宽带信号传输实现
  5. 几十款游戏的简单分析
  6. ECharts 提示框(tooltip)
  7. 软件测试工程师又一大挑战:大数据测试
  8. CAN/以太网转换器 CAN与以太网互联互通
  9. DataBus-入门介绍-1
  10. android webview 禁止放大缩小,在Android WebView中启用/禁用缩放