概况

Android在4.3的版本号中(即API 18)增加了NotificationListenerService,依据SDK的描写叙述(AndroidDeveloper)能够知道,当系统收到新的通知或者通知被删除时,会触发NotificationListenerService的回调方法。同一时候在Android
4.4 中新增了Notification.extras 字段,也就是说能够使用NotificationListenerService获取系统通知详细信息,这在曾经是须要用反射来实现的。

转载请务必注明出处:http://blog.csdn.net/yihongyuelan

重要关系

对于系统通知,三方APP使用NotificationListenerService主要目的是为了获取系统通知相关信息,主要包含:通知的新增和删除,获取当前通知数量,通知内容相关信息等。这些信息能够通过NotificationListenerService类提供的方法以及StatusBarNotification类对象来获取。

NotificationListenerService主要方法(成员变量):

cancelAllNotifications() :删除系统中全部可被清除的通知; 

cancelNotification(String pkg, String tag, int id) :删除详细某一个通知;

getActiveNotifications() :返回当前系统全部通知到StatusBarNotification[];

onNotificationPosted(StatusBarNotification sbn) :当系统收到新的通知后出发回调; 

onNotificationRemoved(StatusBarNotification sbn) :当系统通知被删掉后出发回调;

以上是NotificationListenerService的主要方法,通过这些方法就能够在应用中操作系统通知,在NotificationListenerService中除了对通知的操作之外,还能够获取到通知的StatusBarNotification对象,通过该对象能够获取通知更具体的数据。

StatusBarNotification主要方法(成员变量):

getId():返回通知相应的id;

getNotification():返回通知对象;

getPackageName():返回通知相应的包名;

getPostTime():返回通知发起的时间;

getTag():返回通知的Tag,假设没有设置返回null;

getUserId():返回UserId,用于多用户场景;

isClearable():返回该通知是否可被清楚,FLAG_ONGOING_EVENT、FLAG_NO_CLEAR;

isOngoing():检查该通知的flag是否为FLAG_ONGOING_EVENT;

使用简单介绍

正确使用NotificationListenerService须要注意三点:

(1). 新建一个类并继承自NotificationListenerService,override当中重要的两个方法;

public class NotificationMonitor extends NotificationListenerService {
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
Log.i("SevenNLS","Notification posted");
} @Override
public void onNotificationRemoved(StatusBarNotification sbn) {
Log.i("SevenNLS","Notification removed");
}
}

(2). 在AndroidManifest.xml中注冊Service并声明相关权限;

 <service android:name=".NotificationMonitor"
android:label="@string/service_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>

(3). 开启NotificationMonitor的监听功能;

完毕以上两步之后,将程序编译并安装到手机上,但此时该程序是无法监听到新增通知和删除通知的,还须要在"Settings > Security > Notification access"中,勾选NotificationMonitor。此时假设系统收到新的通知或者通知被删除就会打印出对应的log了。

这里须要注意,假设手机上没有安装使用NotificationListenerService类的APP,Notification access是不会显示出来的。能够在源代码/packages/apps/Settings/src/com/android/settings/SecuritySettings.java中看到,假设没有使用NotificationListenerService的APK,直接就不显示这一项了。

mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);
if (mNotificationAccess != null) {
final int total = NotificationAccessSettings.getListenersCount(mPM);
if (total == 0) {
if (deviceAdminCategory != null) {
deviceAdminCategory.removePreference(mNotificationAccess);
}
} else {
final int n = getNumEnabledNotificationListeners();
if (n == 0) {
mNotificationAccess.setSummary(getResources().getString(
R.string.manage_notification_access_summary_zero));
} else {
mNotificationAccess.setSummary(String.format(getResources().getQuantityString(
R.plurals.manage_notification_access_summary_nonzero,
n, n)));
}
}
}

使用具体解释

通过前面的解说(实际上就是对AndroidDeveloper的解释),已经能够正常使用NotificationListenerService了,但对于实际应用中,须要考虑的事情还比較多。比方:

1. 怎样检測应用已开启Notification access监听功能?

假设检測到应用没有激活Notification access监听功能,须要提示用户开启;

2. 能不能主动跳转到Notification access监听页面?

假设可以依据第1步的推断自己主动跳转到相应的页面,那可以省掉非常多操作;

3. 怎样与NotificationListenerService交互?

涉及到与Service的交互,但又与普通的Service不同,这里后文解释;

4. NotificationListenerService使用过程中有哪些注意事项?

在使用NotificationListenerService过程中自己遇到了一些坑,后文会通过分析给出对应的解决方式;

程序执行截图

 

图 1 程序执行截图

演示样例介绍

NotificationListenerDemo主要用于获取系统当前通知信息,并可手动创建"可清除通知",逐条删除"可清除通知",一次性删除"可清除通知",以及显示系统当前活动的通知信息。实际上该演示样例回答了前面使用具体解释中提出的各项疑问,在实际使用过程中相信大部分人都会遇到,因此这里逐条展开与大家分享。

图 2 主界面

功能分析

1. 怎样检測应用已开启Notification access监听功能?

在程序启动时,运行Notification access的检測,查看是否訪问Notification的权限。假设用户没有Enable Notification access,则弹出提示对话框,点击OK跳转到Notification access设置页面。

图 3 首次启动 isEnable

使用NotificationListenerService的应用假设开启了Notification access,系统会将包名等相关信息写入SettingsProver数据库中,因此能够从数据库中获取相关信息并过滤,从而推断应用是否开启了Notification access,代码例如以下:

private static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
private boolean isEnabled() {
String pkgName = getPackageName();
final String flat = Settings.Secure.getString(getContentResolver(),
ENABLED_NOTIFICATION_LISTENERS);
if (!TextUtils.isEmpty(flat)) {
final String[] names = flat.split(":");
for (int i = 0; i < names.length; i++) {
final ComponentName cn = ComponentName.unflattenFromString(names[i]);
if (cn != null) {
if (TextUtils.equals(pkgName, cn.getPackageName())) {
return true;
}
}
}
}
return false;
}

在返回值flat中假设包括了应用的包名,就可以确定应用已开启Notification access,反之则表示没有开启。

2. 能不能主动跳转到Notification access监听页面?

通过查看能够知道,Notification access界面接收action为"android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"的intent启动,因此使用startActivity能够非常easy的跳转到该页面,从而避免用户在Settings中查找。代码例如以下:

private static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
private void openNotificationAccess() {
startActivity(new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS));
}

3. 怎样与NotificationListenerService交互?

由于NotificationListenerService中包括了四个重要的方法,各自是:onNotificationPosted、onNotificationRemoved、cancelNotification、cancelAllNotifications。通过这些方法我们才干实现诸如通知信息的获取以及删除等功能,尽管这些方法是public的,那是不是意味着我们仅仅要拿到NotificationListenerService的对象就能够直接调用这些方法了呢?那怎样拿到Service的对象呢?在之前的博文中,曾有提到与Service的交互(
详细可參考拙作《Android中程序与Service交互的方式——交互方式》),能够看到与Service的交互有非常多种方法,但假设要拿到Service的对象,归根究竟还是须要Binder。

也就是说得使用bindService的办法,将onServiceConnected回调中的IBinder对象转型成NotificationListenerService的对象。測试代码例如以下:

//在MainActivity.java的onCreate方法中使用bindService帮顶NotificationMonitor服务
bindService(new Intent(this,NotificationMonitor.class ), new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName arg0) {
} @Override
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
NotificationMonitor.MyBinder localBinder = (MyBinder)arg1;
NotificationMonitor mMonitor = localBinder.getService();
}
}, BIND_AUTO_CREATE);
//NotificationMonitor的onBind方法返回构造的Binder对象
public class NotificationMonitor extends NotificationListenerService {
private MyBinder mBinder = new MyBinder();
public class MyBinder extends Binder{
public NotificationMonitor getService(){
return NotificationMonitor.this;
}
} @Override
public IBinder onBind(Intent arg0) {
return mBinder;
} @Override
public void onNotificationPosted(StatusBarNotification sbn) {
getActiveNotifications();
cancelAllNotifications();
} @Override
public void onNotificationRemoved(StatusBarNotification sbn) {
}
}

那这样操作之后是不是就意味着能够拿到NotificationMonitor的对象并直接调用getActiveNotifications()方法,用于获取当前系统通知的信息了呢?非常抱歉,事实证明这样是不行的。这里简单的分析下,在后面的NotificationListenerService原理分析中再具体解说。在NotificationListenerService的源代码中能够看到:

@Override
public IBinder onBind(Intent intent) {
if (mWrapper == null) {
mWrapper = new INotificationListenerWrapper();
}
return mWrapper;
}

这里的INotificationListenerWrapper是NotificationListenerService的一个内部类:

private class INotificationListenerWrapper extends INotificationListener.Stub

而NotificationMonitor继承自NotificationListenerService,默认的onBind方法却是:

@Override
public IBinder onBind(Intent intent) {
return super.onBind(intent);
}

这里注意,普通情况下service的onBind方法返回要么是null要么是Binder对象,可这里直接调用父类NotificationListenerService的onBind方法,而父类返回的是INotificationListenerWrapper的对象。这说明Binder对象已经被指定了,不能再给NotificationMonitor指定其他的Binder对象。假设你非要给NotificationMonitor指定其他的Binder对象,那么就无法使用INotificationListenerWrapper提供的方法。也就是说要么就用系统NotificationListenerService提供的方法,要么就把NotificationMonitor当一个普通的Service来用,系统提供的方法都不能使用。

那应该怎样使用NotificationListenerService中的方法呢?在拙作《Android中程序与Service交互的方式——交互方式》中,已经提供了非常多的样例,这里仅以广播的方式为例。

既然NotificationMonitor能够使用NotificationListenerService的方法,那通过NotificationMonitor把通知状态的改变以及数据获取到,并使用static数据进行存储,之后再在MainActivity中直接使用就可以。在MainActivity中控制通知的单个删除和所有删除,则使用广播的方式发送给NotificationMonitor进行处理。MainActivity与NotificationMonitor的关系类图例如以下:

图 4 结构类图

NotificationMonitor和MainActivity关键代码例如以下:

public class NotificationMonitor extends NotificationListenerService {
private static final String TAG = "SevenNLS";
private static final String TAG_PRE = "[" + NotificationMonitor.class.getSimpleName() + "] ";
private static final int EVENT_UPDATE_CURRENT_NOS = 0;
public static final String ACTION_NLS_CONTROL = "com.seven.notificationlistenerdemo.NLSCONTROL";
//用于存储当前全部的Notification的StatusBarNotification对象数组
public static List<StatusBarNotification[]> mCurrentNotifications = new ArrayList<StatusBarNotification[]>();
public static int mCurrentNotificationsCounts = 0;
//收到新通知后将通知的StatusBarNotification对象赋值给mPostedNotification
public static StatusBarNotification mPostedNotification;
//删除一个通知后将通知的StatusBarNotification对象赋值给mRemovedNotification
public static StatusBarNotification mRemovedNotification;
private CancelNotificationReceiver mReceiver = new CancelNotificationReceiver();
// String a;
private Handler mMonitorHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_UPDATE_CURRENT_NOS:
updateCurrentNotifications();
break;
default:
break;
}
}
}; class CancelNotificationReceiver extends BroadcastReceiver { @Override
public void onReceive(Context context, Intent intent) {
String action;
if (intent != null && intent.getAction() != null) {
action = intent.getAction();
if (action.equals(ACTION_NLS_CONTROL)) {
String command = intent.getStringExtra("command");
if (TextUtils.equals(command, "cancel_last")) {
if (mCurrentNotifications != null && mCurrentNotificationsCounts >= 1) {
//每次删除通知最后一个
StatusBarNotification sbnn = getCurrentNotifications()[mCurrentNotificationsCounts - 1];
cancelNotification(sbnn.getPackageName(), sbnn.getTag(), sbnn.getId());
}
} else if (TextUtils.equals(command, "cancel_all")) {
//删除全部通知
cancelAllNotifications();
}
}
}
} } @Override
public void onCreate() {
super.onCreate();
logNLS("onCreate...");
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_NLS_CONTROL);
registerReceiver(mReceiver, filter);
//在onCreate时第一次调用getActiveNotifications()
mMonitorHandler.sendMessage(mMonitorHandler.obtainMessage(EVENT_UPDATE_CURRENT_NOS));
} @Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mReceiver);
} @Override
public IBinder onBind(Intent intent) {
// a.equals("b");
logNLS("onBind...");
return super.onBind(intent);
} @Override
public void onNotificationPosted(StatusBarNotification sbn) {
//当系统收到新的通知后,更新mCurrentNotifications列表
updateCurrentNotifications();
logNLS("onNotificationPosted...");
logNLS("have " + mCurrentNotificationsCounts + " active notifications");
mPostedNotification = sbn;
//通过下面方式能够获取Notification的具体信息
/*
* Bundle extras = sbn.getNotification().extras; String
* notificationTitle = extras.getString(Notification.EXTRA_TITLE);
* Bitmap notificationLargeIcon = ((Bitmap)
* extras.getParcelable(Notification.EXTRA_LARGE_ICON)); Bitmap
* notificationSmallIcon = ((Bitmap)
* extras.getParcelable(Notification.EXTRA_SMALL_ICON)); CharSequence
* notificationText = extras.getCharSequence(Notification.EXTRA_TEXT);
* CharSequence notificationSubText =
* extras.getCharSequence(Notification.EXTRA_SUB_TEXT);
* Log.i("SevenNLS", "notificationTitle:"+notificationTitle);
* Log.i("SevenNLS", "notificationText:"+notificationText);
* Log.i("SevenNLS", "notificationSubText:"+notificationSubText);
* Log.i("SevenNLS",
* "notificationLargeIcon is null:"+(notificationLargeIcon == null));
* Log.i("SevenNLS",
* "notificationSmallIcon is null:"+(notificationSmallIcon == null));
*/
} @Override
public void onNotificationRemoved(StatusBarNotification sbn) {
//当有通知被删除后,更新mCurrentNotifications列表
updateCurrentNotifications();
logNLS("removed...");
logNLS("have " + mCurrentNotificationsCounts + " active notifications");
mRemovedNotification = sbn;
} private void updateCurrentNotifications() {
try {
StatusBarNotification[] activeNos = getActiveNotifications();
if (mCurrentNotifications.size() == 0) {
mCurrentNotifications.add(null);
}
mCurrentNotifications.set(0, activeNos);
mCurrentNotificationsCounts = activeNos.length;
} catch (Exception e) {
logNLS("Should not be here!!");
e.printStackTrace();
}
} //获取当前状态栏显示通知总数
public static StatusBarNotification[] getCurrentNotifications() {
if (mCurrentNotifications.size() == 0) {
logNLS("mCurrentNotifications size is ZERO!!");
return null;
}
return mCurrentNotifications.get(0);
} private static void logNLS(Object object) {
Log.i(TAG, TAG_PRE + object);
} }

而MainActivity主要负责界面显示与交互,关键代码例如以下:

public class MainActivity extends Activity {

    private static final String TAG = "SevenNLS";
private static final String TAG_PRE = "["+MainActivity.class.getSimpleName()+"] ";
private static final int EVENT_SHOW_CREATE_NOS = 0;
private static final int EVENT_LIST_CURRENT_NOS = 1;
private static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
private static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
private boolean isEnabledNLS = false;
private TextView mTextView; private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_SHOW_CREATE_NOS:
//显示创建的Notification相应的pkgName、Tag、Id
showCreateNotification();
break;
case EVENT_LIST_CURRENT_NOS:
//显示当前全部的Notification数量及其包名
listCurrentNotification();
break; default:
break;
}
}
}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.textView);
} @Override
protected void onResume() {
super.onResume();
//推断是否有开启Notification access
isEnabledNLS = isEnabled();
logNLS("isEnabledNLS = " + isEnabledNLS);
if (!isEnabledNLS) {
//假设没有开启则显示确认对话框
showConfirmDialog();
}
} public void buttonOnClicked(View view) {
mTextView.setTextColor(Color.BLACK);
switch (view.getId()) {
case R.id.btnCreateNotify:
logNLS("Create notifications...");
//创建可清除的Notification
createNotification(this);
//显示当前状态栏中全部Notification数量及其包名
mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_CREATE_NOS), 50);
break;
case R.id.btnClearLastNotify:
logNLS("Clear Last notification...");
//清除最后一个Notification
clearLastNotification();
//显示当前状态栏中全部Notification数量及其包名
mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_LIST_CURRENT_NOS), 50);
break;
case R.id.btnClearAllNotify:
logNLS("Clear All notifications...");
//清除全部"可被清除"的Notification
clearAllNotifications();
mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_LIST_CURRENT_NOS), 50);
break;
case R.id.btnListNotify:
logNLS("List notifications...");
listCurrentNotification();
break;
case R.id.btnEnableUnEnableNotify:
logNLS("Enable/UnEnable notification...");
//打开Notification access启动/取消界面
openNotificationAccess();
break;
default:
break;
}
} //......省略
}

4. NotificationListenerService使用过程中有哪些注意事项?

假设细心察看代码的童鞋,一定发现代码中有使用Handler,以及一些奇怪但又被凝视掉的代码,比方"a.equals("b")"。从使用上来说,没有必要使用handler,那干嘛要多次一举?这里就给大家分享一下在写NotificationListenerDemo时遇到的一些坑。

①. NotificationMonitor的onCreate方法中使用handler来调用getActiveNotifications()方法

若直接在onCreate或者onBind方法中调用getActiveNotifications()方法是无法获取当前系统通知。主要是由于NotificationMonitor还未完毕初始化,而根本原因则是INotificationListenerWrapper对象mWrapper还未初始化,此时使用getActiveNotifications()方法又会调用到mWrapper,因此无法返回正常数据。在NotificationListenerService中能够看到getActiveNotifications()的源代码:

public StatusBarNotification[] getActiveNotifications() {
try {
return getNotificationInterface().getActiveNotificationsFromListener(mWrapper);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
return null;
}

也就是说仅仅要在onBind方法完毕之后,再调用getActiveNotifications()方法就能够正常获取数据了,因此这里使用了handler多线程的方式。当然,为了保险能够使用sendEmptyMessgeDelay加上延时。

②. 假设NotificationMonitor在onCreate或onBind方法中crash,则该service已经失效,需重新启动手机才干进行兴许开发验证

假设在onCreate或者onBind方法中,出现异常导致NotificationMonitor发生crash,就算找到问题并将其改正,之后的验证还是无法继续进行的,也就是无法收到通知的新增和删除消息,onNotificationPosted和onNotificationRemoved方法不会被调用。

这也是我在onBind方法中有益凝视导致空指针异常的代码,有兴趣的童鞋能够把凝视去掉后尝试,去掉凝视会导致NotificationListenerDemo异常停止,此时你再加上凝视再次执行NotificationListenerDemo,尽管程序能够正常启动,但无法正常执行NotificationMonitor中的onNotificationPosted和onNotificationRemoved方法。这个涉及NotificationListenerService的原理,后面会另行分析。

③. MainActivity中onClick方法里使用handler操作

当点击删除通知时,系统通知相关状态还未更新,此时还没有回调到NotificationMonitor中,所以获取的数据就还是上一次的数据。为了可以获取到正确的Notification数据,可以使用handler并加上延时,这样再去获取Notification信息时,系统已经触发了NotificationMonitor回调,数据也有正常了。另外,50ms的延时差点儿是感知不到的。

④. 为什么要使用ArrayList来保存StatusBarNotification数组对象

当新增或者删除通知时,会触发onNotificationPosted或onNotificationRemoved回调,在该方法中调用getActiveNotifications()方法用以获取当前系统通知信息。而getActiveNotifications()返回的是StatusBarNotification[]数组,由于这个数组是可变长的,也就是长度会随时变化,因此无法直接存储。使用ArrayList能够非常好的解决问题,在ArrayList对象中加入一个StatusBarNotification[]对象,之后使用ArrayList.set(0,statusbar[])方法对数据进行更新就可以。

总结

NotificationListenerService是Android 4.3 之后新增的接口服务,用于获取系统Notification信息,这在之前的Android版本号是无法直接办到的。在Android 4.4中,添加了Notification.extra变量,使得获取Notification相关信息更加丰富,这些接口的开放更加利于三方应用的使用,但同一时候也会带来一些隐私问题。

本文针对NotificationListenerService的使用进行了具体分析,当然当中不乏有失偏颇的地方,本着互联网知识共享精神也将自己的一些记录公布出来,一来可做笔记,二来希望可以给苦苦寻觅的童鞋一些帮助。

兴许会对NotificationListenerService的原理进行分析,敬请期待。

NotificationMonitor代码免积分下载:下载Demo

为了兴许可以更新,已经代码传到github上,有兴趣的童鞋可以在github上查看,连接戳这里

Android 4.4 KitKat NotificationManagerService使用具体解释与原理分析(一)__使用具体解释的更多相关文章

  1. Android 4.4 KitKat NotificationManagerService使用具体解释与原理分析(二)__原理分析

    前置文章: <Android 4.4 KitKat NotificationManagerService使用具体解释与原理分析(一)__使用具体解释> 转载请务必注明出处:http://b ...

  2. 【构建Android缓存模块】(一)吐槽与原理分析

    http://my.oschina.net/ryanhoo/blog/93285 摘要:在我翻译的Google官方系列教程中,Bitmap系列由浅入深地介绍了如何正确的解码Bitmap,异步线程操作以 ...

  3. Android插件化与热修复(六)-微信Tinker原理分析

    Tinker热修复原理分析 热补丁技术是在用户不需要重新安装应用的情况下实现应用更新,可快速解决一些线上问题.热补丁省去了Android应用发布版本的成本,而且用户端的更新也是无感知的. Tinker ...

  4. Android 学习笔记之WebService实现远程调用+内部原理分析...

    PS:终于可以抽出时间写写博客了,忙着学校的三周破实训外加替考...三周了,没怎么学习...哎... 学习内容: 1.WebService 实现远程方法的调用   什么是WebService...   ...

  5. Android大图片裁剪终极解决方案(上:原理分析)

    转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-)  http://my.oschina.net/ryanhoo/blog/86842 约几个月前,我正 ...

  6. Android窗口管理服务WindowManagerService显示窗口动画的原理分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8611754 在前一文中,我们分析了Activi ...

  7. Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程

    本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处 本文代码以MTK平台Android 4.4为分析对象,与Google原生AOSP有些许差异,请读者知悉. ...

  8. Android 4.4 Kitkat Phone工作流程浅析(七)__来电(MT)响铃流程

    本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处 本文代码以MTK平台Android 4.4为分析对象,与Google原生AOSP有些许差异,请读者知悉. ...

  9. Android 4.4 KitKat 新特性

    New in Android 4.4 KitKat 本文是一个概览,关于KitKat,也即Android4.4的新东西,先是功能型的,之后是设计上的. 很多特性本文并没有提到,很多提到的特性也只是简短 ...

随机推荐

  1. vs2015 安装问题汇总

    1. The product version that you are trying to set up is earlier than the version already installed o ...

  2. 开源Math.NET基础数学类库使用(14)C#生成安全的随机数

    原文:[原创]开源Math.NET基础数学类库使用(14)C#生成安全的随机数                本博客所有文章分类的总目录:http://www.cnblogs.com/asxinyu/ ...

  3. 如何解决Android SDK无法下载Package的问题(.net)

    有些用户在安装好Android SDK后,打开Android SDK Manager下载API时一直显示“Done loading packages”却迟迟不能前进,界面显示的Package空空如也. ...

  4. Java二叉排序树(转)

    一.二叉排序树定义 1.二叉排序树的定义 二叉排序树(Binary Sort Tree)又称二叉查找(搜索)树(Binary Search Tree).其定义为:二叉排序树或者是空树,或者是满足如下性 ...

  5. 【原创】《算法导论》链表一章带星习题试解——附C语言实现

    原题: 双向链表中,需要三个基本数据,一个携带具体数据,一个携带指向上一环节的prev指针,一个携带指向下一环节的next指针.请改写双向链表,仅用一个指针np实现双向链表的功能.定义np为next ...

  6. 使用BackgroundWorker组件进行异步操作编程

    本文介绍了BackgroundWorker组件的功能及在基于事件的异步操作编程中的应用,并对组件的实现原理进行简述.在应用程序中,可能会遇到一些执行耗时的功能操作,比如数据下载.复杂计算及数据库事务等 ...

  7. 使用 WPF 创建单实例应用程序

    一个简单的例子就是大家在使用很多应用程序,例如在使用Microsoft Word 时会遇到一种情况,不管你打开多少个文档,系统一次只能加载一个winword.exe 实例.当打开新文档时,文档在新窗口 ...

  8. 全新E:网站不是之前排名浮动 相比于竞争对手究竟缺少了什么?

    这几天有非常多朋友问新辰,为什么站点排名掉了?为什么被人家逆袭反超了?当然,这无疑与你站点的内容.外链和用户体验有非常大关系,只是.新辰在此觉得,还须要多研究一下竞争对手的站点,做到:人无我有.人有我 ...

  9. java提高篇(十)-----强制类型转换

    在java中强制类型转换分为基本数据类型和引用数据类型两种,这里我们讨论的后者,也就是引用数据类型的强制类型转换. 在Java中由于继承和向上转型,子类可以非常自然地转换成父类,但是父类转换成子类则需 ...

  10. 集差集 哈希表 比较数据库的照片和server画面上,将server垃圾上的图片删除

    SSH 骨架code: public String deleRubbishAd(){ int deleADcount = 0; rubbishADtp = configDao.rubbishADtp( ...