Android灯光系统(硬件访问服务框架)

Java类:LightsService.java

LightsService.java通过调用,LightsService JNI来实现com.android.server包中的LightsService类。

这个类不是平台API,被Android系统JAVA框架中的其他一些部分调用。

/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.android.server.lights;import android.app.ActivityManager;import android.content.Context;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.os.PowerManager;import android.os.Trace;import android.provider.Settings;import android.util.Slog;import android.view.SurfaceControl;import com.android.server.SystemService;public class LightsService extends SystemService {         static final String TAG = "LightsService";    static final boolean DEBUG = false;    final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT];    private final class LightImpl extends Light {             private final IBinder mDisplayToken;        private final int mSurfaceControlMaximumBrightness;        private LightImpl(Context context, int id) {                 mId = id;            mDisplayToken = SurfaceControl.getInternalDisplayToken();            final boolean brightnessSupport = SurfaceControl.getDisplayBrightnessSupport(                    mDisplayToken);            if (DEBUG) {                     Slog.d(TAG, "Display brightness support: " + brightnessSupport);            }            int maximumBrightness = 0;            if (brightnessSupport) {                     PowerManager pm = context.getSystemService(PowerManager.class);                if (pm != null) {                         maximumBrightness = pm.getMaximumScreenBrightnessSetting();                }            }            mSurfaceControlMaximumBrightness = maximumBrightness;        }        @Override        public void setBrightness(int brightness) {                 setBrightness(brightness, BRIGHTNESS_MODE_USER);        }        @Override        public void setBrightness(int brightness, int brightnessMode) {                 synchronized (this) {                     // LOW_PERSISTENCE cannot be manually set                if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {                         Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId +                            ": brightness=0x" + Integer.toHexString(brightness));                    return;                }                // Ideally, we'd like to set the brightness mode through the SF/HWC as well, but                // right now we just fall back to the old path through Lights brightessMode is                // anything but USER or the device shouldBeInLowPersistenceMode().                if (brightnessMode == BRIGHTNESS_MODE_USER && !shouldBeInLowPersistenceMode()                        && mSurfaceControlMaximumBrightness == 255) {                         // TODO: the last check should be mSurfaceControlMaximumBrightness != 0; the                    // reason we enforce 255 right now is to stay consistent with the old path. In                    // the future, the framework should be refactored so that brightness is a float                    // between 0.0f and 1.0f, and the actual number of supported brightness levels                    // is determined in the device-specific implementation.                    if (DEBUG) {                             Slog.d(TAG, "Using new setBrightness path!");                    }                    SurfaceControl.setDisplayBrightness(mDisplayToken,                            (float) brightness / mSurfaceControlMaximumBrightness);                } else {                         int color = brightness & 0x000000ff;                    color = 0xff000000 | (color << 16) | (color << 8) | color;                    setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);                }            }        }        @Override        public void setColor(int color) {                 synchronized (this) {                     setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, 0);            }        }        @Override        public void setFlashing(int color, int mode, int onMS, int offMS) {                 synchronized (this) {                     setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER);            }        }        @Override        public void pulse() {                 pulse(0x00ffffff, 7);        }        @Override        public void pulse(int color, int onMS) {                 synchronized (this) {                     if (mColor == 0 && !mFlashing) {                         setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000,                            BRIGHTNESS_MODE_USER);                    mColor = 0;                    mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS);                }            }        }        @Override        public void turnOff() {                 synchronized (this) {                     setLightLocked(0, LIGHT_FLASH_NONE, 0, 0, 0);            }        }        @Override        public void setVrMode(boolean enabled) {                 synchronized (this) {                     if (mVrModeEnabled != enabled) {                         mVrModeEnabled = enabled;                    mUseLowPersistenceForVR =                            (getVrDisplayMode() == Settings.Secure.VR_DISPLAY_MODE_LOW_PERSISTENCE);                    if (shouldBeInLowPersistenceMode()) {                             mLastBrightnessMode = mBrightnessMode;                    }                    // NOTE: We do not trigger a call to setLightLocked here.  We do not know the                    // current brightness or other values when leaving VR so we avoid any incorrect                    // jumps. The code that calls this method will immediately issue a brightness                    // update which is when the change will occur.                }            }        }        private void stopFlashing() {                 synchronized (this) {                     setLightLocked(mColor, LIGHT_FLASH_NONE, 0, 0, BRIGHTNESS_MODE_USER);            }        }        private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {                 if (shouldBeInLowPersistenceMode()) {                     brightnessMode = BRIGHTNESS_MODE_LOW_PERSISTENCE;            } else if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {                     brightnessMode = mLastBrightnessMode;            }            if (!mInitialized || color != mColor || mode != mMode || onMS != mOnMS ||                    offMS != mOffMS || mBrightnessMode != brightnessMode) {                     if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"                        + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode);                mInitialized = true;                mLastColor = mColor;                mColor = color;                mMode = mode;                mOnMS = onMS;                mOffMS = offMS;                mBrightnessMode = brightnessMode;                Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"                        + Integer.toHexString(color) + ")");                try {                         setLight_native(mId, color, mode, onMS, offMS, brightnessMode);                } finally {                         Trace.traceEnd(Trace.TRACE_TAG_POWER);                }            }        }        private boolean shouldBeInLowPersistenceMode() {                 return mVrModeEnabled && mUseLowPersistenceForVR;        }        private int mId;        private int mColor;        private int mMode;        private int mOnMS;        private int mOffMS;        private boolean mFlashing;        private int mBrightnessMode;        private int mLastBrightnessMode;        private int mLastColor;        private boolean mVrModeEnabled;        private boolean mUseLowPersistenceForVR;        private boolean mInitialized;    }    public LightsService(Context context) {             super(context);        for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) {                 mLights[i] = new LightImpl(context, i);        }    }    @Override    public void onStart() {             publishLocalService(LightsManager.class, mService);    }    @Override    public void onBootPhase(int phase) {         }    private int getVrDisplayMode() {             int currentUser = ActivityManager.getCurrentUser();        return Settings.Secure.getIntForUser(getContext().getContentResolver(),                Settings.Secure.VR_DISPLAY_MODE,                /*default*/Settings.Secure.VR_DISPLAY_MODE_LOW_PERSISTENCE,                currentUser);    }    private final LightsManager mService = new LightsManager() {             @Override        public Light getLight(int id) {                 if (0 <= id && id < LIGHT_ID_COUNT) {                     return mLights[id];            } else {                     return null;            }        }    };    private Handler mH = new Handler() {             @Override        public void handleMessage(Message msg) {                 LightImpl light = (LightImpl)msg.obj;            light.stopFlashing();        }    };    static native void setLight_native(int light, int color, int mode,            int onMS, int offMS, int brightnessMode);}

本地类:com_android_server_lights_LightsService.cpp

这个类调用硬件抽象层,也同时提供了JNI的接口。

/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#define LOG_TAG "LightsService"#include "jni.h"#include #include "android_runtime/AndroidRuntime.h"#include #include #include #include #include #include #include namespace android {     using Brightness = ::android::hardware::light::V2_0::Brightness;using Flash      = ::android::hardware::light::V2_0::Flash;using ILight     = ::android::hardware::light::V2_0::ILight;using LightState = ::android::hardware::light::V2_0::LightState;using Status     = ::android::hardware::light::V2_0::Status;using Type       = ::android::hardware::light::V2_0::Type;template<typename T>using Return     = ::android::hardware::Return<T>;static bool sLightSupported = true;static bool validate(jint light, jint flash, jint brightness) {         bool valid = true;    if (light < 0 || light >= static_cast<jint>(Type::COUNT)) {             ALOGE("Invalid light parameter %d.", light);        valid = false;    }    if (flash != static_cast<jint>(Flash::NONE) &&        flash != static_cast<jint>(Flash::TIMED) &&        flash != static_cast<jint>(Flash::HARDWARE)) {             ALOGE("Invalid flash parameter %d.", flash);        valid = false;    }    if (brightness != static_cast<jint>(Brightness::USER) &&        brightness != static_cast<jint>(Brightness::SENSOR) &&        brightness != static_cast<jint>(Brightness::LOW_PERSISTENCE)) {             ALOGE("Invalid brightness parameter %d.", brightness);        valid = false;    }    if (brightness == static_cast<jint>(Brightness::LOW_PERSISTENCE) &&        light != static_cast<jint>(Type::BACKLIGHT)) {             ALOGE("Cannot set low-persistence mode for non-backlight device.");        valid = false;    }    return valid;}static LightState constructState(        jint colorARGB,        jint flashMode,        jint onMS,        jint offMS,        jint brightnessMode){         Flash flash = static_cast<Flash>(flashMode);    Brightness brightness = static_cast<Brightness>(brightnessMode);    LightState state{     };    if (brightness == Brightness::LOW_PERSISTENCE) {             state.flashMode = Flash::NONE;    } else {             // Only set non-brightness settings when not in low-persistence mode        state.flashMode = flash;        state.flashOnMs = onMS;        state.flashOffMs = offMS;    }    state.color = colorARGB;    state.brightnessMode = brightness;    return state;}static void processReturn(        const Return<Status> &ret,        Type type,        const LightState &state) {         if (!ret.isOk()) {             ALOGE("Failed to issue set light command.");        return;    }    switch (static_cast<Status>(ret)) {             case Status::SUCCESS:            break;        case Status::LIGHT_NOT_SUPPORTED:            ALOGE("Light requested not available on this device. %d", type);            break;        case Status::BRIGHTNESS_NOT_SUPPORTED:            ALOGE("Brightness parameter not supported on this device: %d",                state.brightnessMode);            break;        case Status::UNKNOWN:        default:            ALOGE("Unknown error setting light.");    }}static void setLight_native(        JNIEnv* /* env */,        jobject /* clazz */,        jint light,        jint colorARGB,        jint flashMode,        jint onMS,        jint offMS,        jint brightnessMode) {         if (!sLightSupported) {             return;    }    if (!validate(light, flashMode, brightnessMode)) {             return;    }    Type type = static_cast<Type>(light);    LightState state = constructState(        colorARGB, flashMode, onMS, offMS, brightnessMode);    {             android::base::Timer t;        sp<ILight> hal = ILight::getService();        if (hal == nullptr) {                 sLightSupported = false;            return;        }        Return<Status> ret = hal->setLight(type, state);        processReturn(ret, type, state);        if (t.duration() > 50ms) ALOGD("Excessive delay setting light");    }}static const JNINativeMethod method_table[] = {         {      "setLight_native", "(IIIIII)V", (void*)setLight_native },};int register_android_server_LightsService(JNIEnv *env) {         return jniRegisterNativeMethods(env, "com/android/server/lights/LightsService",            method_table, NELEM(method_table));}};

硬件抽象层:light.c

/* * Copyright (C) 2008 The Android Open Source Project * Copyright (C) 2014 The  Linux Foundation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */#include #include #include #include #include #include #include #include #include #include #include #include #include /* * Change this to 1 to support battery notifications via BatteryService */#define LIGHTS_SUPPORT_BATTERY 0#define CG_COLOR_ID_PROPERTY "ro.boot.hardware.color"static pthread_once_t g_init = PTHREAD_ONCE_INIT;static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;static struct light_state_t g_notification;static struct light_state_t g_battery;static int g_last_backlight_mode = BRIGHTNESS_MODE_USER;static int g_attention = 0;static int rgb_brightness_ratio = 255;char const*const RED_LED_FILE        = "/sys/class/leds/red/brightness";char const*const GREEN_LED_FILE        = "/sys/class/leds/green/brightness";char const*const BLUE_LED_FILE        = "/sys/class/leds/blue/brightness";char const*const LCD_FILE        = "/sys/class/leds/lcd-backlight/brightness";char const*const PERSISTENCE_FILE        = "/sys/class/leds/lcd-backlight/low_persistence";char const*const RED_BLINK_FILE        = "/sys/class/leds/red/blink";char const*const GREEN_BLINK_FILE        = "/sys/class/leds/green/blink";char const*const BLUE_BLINK_FILE        = "/sys/class/leds/blue/blink";char const*const RED_ON_OFF_MS_FILE        = "/sys/class/leds/red/on_off_ms";char const*const GREEN_ON_OFF_MS_FILE        = "/sys/class/leds/green/on_off_ms";char const*const BLUE_ON_OFF_MS_FILE        = "/sys/class/leds/blue/on_off_ms";char const*const RED_RGB_START_FILE        = "/sys/class/leds/red/rgb_start";char const*const GREEN_RGB_START_FILE        = "/sys/class/leds/green/rgb_start";char const*const BLUE_RGB_START_FILE        = "/sys/class/leds/blue/rgb_start";/** * device methods */void init_globals(void){         char color_id_prop[PROPERTY_VALUE_MAX] = {     ""};    // init the mutex    pthread_mutex_init(&g_lock, NULL);    // check CG color    property_get(CG_COLOR_ID_PROPERTY, color_id_prop, "DEF00");    if (strcmp(color_id_prop, "GRA00") == 0) {             rgb_brightness_ratio = 25;    } else if (strcmp(color_id_prop, "SLV00") == 0) {             rgb_brightness_ratio = 15;    } else if (strcmp(color_id_prop, "BLU00") == 0) {             rgb_brightness_ratio = 15;    } else {             rgb_brightness_ratio = 20;    }}static intwrite_int(char const* path, int value){         int fd;    static int already_warned = 0;    fd = open(path, O_WRONLY);    if (fd >= 0) {             char buffer[20];        size_t bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);        if(bytes >= sizeof(buffer)) return -EINVAL;        ssize_t amt = write(fd, buffer, bytes);        close(fd);        return amt == -1 ? -errno : 0;    } else {             if (already_warned == 0) {                 ALOGE("write_int failed to open %s\n", path);            already_warned = 1;        }        return -errno;    }}static intwrite_double_int(char const* path, int value1, int value2){         int fd;    static int already_warned = 0;    fd = open(path, O_WRONLY);    if (fd >= 0) {             char buffer[20];        size_t bytes = snprintf(buffer, sizeof(buffer), "%d %d\n", value1, value2);        if(bytes >= sizeof(buffer)) return -EINVAL;        ssize_t amt = write(fd, buffer, bytes);        close(fd);        return amt == -1 ? -errno : 0;    } else {             if (already_warned == 0) {                 ALOGE("write_int failed to open %s\n", path);            already_warned = 1;        }        return -errno;    }}static intis_lit(struct light_state_t const* state){         return state->color & 0x00ffffff;}static intrgb_to_brightness(struct light_state_t const* state){         int color = state->color & 0x00ffffff;    return ((77*((color>>16)&0x00ff))            + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;}static intset_light_backlight(struct light_device_t* dev,        struct light_state_t const* state){         int err = 0;    int brightness = rgb_to_brightness(state);    unsigned int lpEnabled = state->brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE;    if(!dev) {             return -1;    }    pthread_mutex_lock(&g_lock);    // If we're not in lp mode and it has been enabled or if we are in lp mode    // and it has been disabled send an ioctl to the display with the update    if ((g_last_backlight_mode != state->brightnessMode && lpEnabled) ||            (!lpEnabled && g_last_backlight_mode == BRIGHTNESS_MODE_LOW_PERSISTENCE)) {             if ((err = write_int(PERSISTENCE_FILE, lpEnabled)) != 0) {                 ALOGE("%s: Failed to write to %s: %s\n", __FUNCTION__, PERSISTENCE_FILE,                    strerror(errno));        }    }    g_last_backlight_mode = state->brightnessMode;    if (!err) {             err = write_int(LCD_FILE, brightness);    }    pthread_mutex_unlock(&g_lock);    return err;}static intset_speaker_light_locked(struct light_device_t* dev,        struct light_state_t const* state){         int red, green, blue;    int blink;    int onMS, offMS;    unsigned int colorRGB;    if(!dev) {             return -1;    }    switch (state->flashMode) {             case LIGHT_FLASH_TIMED:            onMS = state->flashOnMS;            offMS = state->flashOffMS;            break;        case LIGHT_FLASH_NONE:        default:            onMS = 0;            offMS = 0;            break;    }    colorRGB = state->color;#if 0    ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n",            state->flashMode, colorRGB, onMS, offMS);#endif    red = ((colorRGB >> 16) & 0xFF) * rgb_brightness_ratio / 255;    green = ((colorRGB >> 8) & 0xFF) * rgb_brightness_ratio / 255;    blue = (colorRGB & 0xFF) * rgb_brightness_ratio / 255;    write_double_int(RED_ON_OFF_MS_FILE, onMS, offMS);    write_int(RED_LED_FILE, red);    write_double_int(GREEN_ON_OFF_MS_FILE, onMS, offMS);    write_int(GREEN_LED_FILE, green);    write_double_int(BLUE_ON_OFF_MS_FILE, onMS, offMS);    write_int(BLUE_LED_FILE, blue);    if(!write_int(RED_RGB_START_FILE, 1))        if(!write_int(GREEN_RGB_START_FILE, 1))            if(!write_int(BLUE_RGB_START_FILE, 1))                return -1;    return 0;}static voidhandle_speaker_battery_locked(struct light_device_t* dev){         if (is_lit(&g_battery)) {             set_speaker_light_locked(dev, &g_battery);    } else {             set_speaker_light_locked(dev, &g_notification);    }}#if LIGHTS_SUPPORT_BATTERYstatic intset_light_battery(struct light_device_t* dev,        struct light_state_t const* state){         pthread_mutex_lock(&g_lock);    g_battery = *state;    handle_speaker_battery_locked(dev);    pthread_mutex_unlock(&g_lock);    return 0;}#endifstatic intset_light_notifications(struct light_device_t* dev,        struct light_state_t const* state){         pthread_mutex_lock(&g_lock);    g_notification = *state;    handle_speaker_battery_locked(dev);    pthread_mutex_unlock(&g_lock);    return 0;}static intset_light_attention(struct light_device_t* dev,        struct light_state_t const* state){         pthread_mutex_lock(&g_lock);    if (state->flashMode == LIGHT_FLASH_HARDWARE) {             g_attention = state->flashOnMS;    } else if (state->flashMode == LIGHT_FLASH_NONE) {             g_attention = 0;    }    handle_speaker_battery_locked(dev);    pthread_mutex_unlock(&g_lock);    return 0;}/** Close the lights device */static intclose_lights(struct light_device_t *dev){         if (dev) {             free(dev);    }    return 0;}/******************************************************************************//** * module methods *//** Open a new instance of a lights device using name */static int open_lights(const struct hw_module_t* module, char const* name,        struct hw_device_t** device){         int (*set_light)(struct light_device_t* dev,            struct light_state_t const* state);    if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))        set_light = set_light_backlight;#if LIGHTS_SUPPORT_BATTERY    else if (0 == strcmp(LIGHT_ID_BATTERY, name))        set_light = set_light_battery;#endif    else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))        set_light = set_light_notifications;    else if (0 == strcmp(LIGHT_ID_ATTENTION, name))        set_light = set_light_attention;    else        return -EINVAL;    pthread_once(&g_init, init_globals);    struct light_device_t *dev = malloc(sizeof(struct light_device_t));    if(!dev)        return -ENOMEM;    memset(dev, 0, sizeof(*dev));    dev->common.tag = HARDWARE_DEVICE_TAG;    dev->common.version = LIGHTS_DEVICE_API_VERSION_2_0;    dev->common.module = (struct hw_module_t*)module;    dev->common.close = (int (*)(struct hw_device_t*))close_lights;    dev->set_light = set_light;    *device = (struct hw_device_t*)dev;    return 0;}static struct hw_module_methods_t lights_module_methods = {         .open =  open_lights,};/* * The lights Module */struct hw_module_t HAL_MODULE_INFO_SYM = {         .tag = HARDWARE_MODULE_TAG,    .version_major = 1,    .version_minor = 0,    .id = LIGHTS_HARDWARE_MODULE_ID,    .name = "lights Module",    .author = "Google, Inc.",    .methods = &lights_module_methods,};

驱动层:led_driver.c

#include #include #include #include #include #include                                                                                            #include #define PAD_LEDCON 0x01c208b4#define PAD_LEDDAT 0x01c208c4unsigned int *pad_con = NULL;unsigned int *pad_dat = NULL;struct led_classdev * cdev;void leds_brightness_set(struct led_classdev *led_cdev,  enum led_brightness brightness){     if(brightness){     writel((readl(pad_dat)&(~(1<<2))),pad_dat);}else{     writel((readl(pad_dat)|(1<<2)),pad_dat);}}static int __init leds_init(void){     int ret;//1.分配结构体cdev = kzalloc(sizeof(*cdev),GFP_KERNEL);if(cdev == NULL){     printk("alloc led_classdev is fail.\n");return -ENOMEM;}//2.结构体的初始化cdev->name = "myleds";cdev->max_brightness = LED_FULL;cdev->brightness_set = leds_brightness_set;cdev->flags |= LED_CORE_SUSPENDRESUME;//3.硬件相关的操作pad_con = (unsigned int *)ioremap(PAD_LEDCON,4);pad_dat = (unsigned int *)ioremap(PAD_LEDDAT,4);writel(((readl(pad_con)&(~(0xf<<8)))|(1<<8)),pad_con);writel((readl(pad_dat)|(1<<2)),pad_dat);//4.注册ret = led_classdev_register(NULL,cdev);if(ret < 0){     printk("register led_classdev is fail.\n");return -EAGAIN;}return 0;}static void __exit leds_exit(void){     //注销led_classdev_unregister(cdev);}module_init(leds_init);module_exit(leds_exit);MODULE_LICENSE("GPL");

更多相关文章

  1. :activity状态的保存和保持
  2. Android(安卓)  Intent 的几种启动活动的方式
  3. Lua学习 2) —— Android与Lua互调
  4. android api 中文 (75)—— AdapterView.OnItemClickListener
  5. Android四大基本组件介绍与生命周期
  6. Android消息机制不完全解析(上)
  7. Android(安卓)Retrofit 2.0框架上传图片,视频解决方案
  8. 2011.11.25——— android ndk 坑爹的cygwin
  9. Android调用系统短信发送界面并预设接收号码、短信内容

随机推荐

  1. 如何解决在Azure上部署Sqlserver网络访问
  2. MSSQL中进行SQL除法运算结果为小数却显示
  3. SQL Server查询前N条记录的常用方法小结
  4. ASP和SQL Server如何构建网页防火墙
  5. 有关数据库SQL递归查询在不同数据库中的
  6. 记一次公司仓库数据库服务器死锁过程及解
  7. 实用的银行转账存储过程和流水号生成存储
  8. sql server代理中作业执行SSIS包失败的解
  9. SQL Server数据库中批量导入数据的2种方
  10. 浅谈基于SQL Server分页存储过程五种方法