SensorManager

在SensorManager.java文件的开头,有一段sensor应用的示例。应用层获取sensor的数据主要是通过SensorManager的onAccuracyChanged和onSensorChanged两个监听接口。

public class SensorActivity extends Activity, implements SensorEventListener {private final SensorManager mSensorManager;private final Sensor mAccelerometer;public SensorActivity() {mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);}protected void onResume() {super.onResume();mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);}protected void onPause() {super.onPause();mSensorManager.unregisterListener(this);}public void onAccuracyChanged(Sensor sensor, int accuracy) {}public void onSensorChanged(SensorEvent event) {}
}

代码路径:frameworks/base/core/java/android/hardware/SensorManager.java,SensorManager类主要是对外提供sensor的调用接口:getDefaultSensor,registerListener,unregisterListener。从下面的代码里可以看到,这三个接口会调用getFullSensorList,registerListenerImpl,unregisterListenerImpl。实际实现的代码在SystemSensorManager.java里。

public Sensor getDefaultSensor(int type) {// TODO: need to be smarter, for now, just return the 1st sensor// 这里是从所有的sensor中获得对应type的sensor list.List<Sensor> l = getSensorList(type);boolean wakeUpSensor = false;// For the following sensor types, return a wake-up sensor. These types are by default// defined as wake-up sensors. For the rest of the SDK defined sensor types return a// non_wake-up version.// 如果获取的tyep是以下类型,则返回一个wake-up sensor,否则返回non_wake-up sensor。if (type == Sensor.TYPE_PROXIMITY || type == Sensor.TYPE_SIGNIFICANT_MOTION|| type == Sensor.TYPE_TILT_DETECTOR || type == Sensor.TYPE_WAKE_GESTURE|| type == Sensor.TYPE_GLANCE_GESTURE || type == Sensor.TYPE_PICK_UP_GESTURE|| type == Sensor.TYPE_WRIST_TILT_GESTURE|| type == Sensor.TYPE_DYNAMIC_SENSOR_META || type == Sensor.TYPE_HINGE_ANGLE) {wakeUpSensor = true;}for (Sensor sensor : l) {if (sensor.isWakeUpSensor() == wakeUpSensor) return sensor;}return null;
}
public List<Sensor> getSensorList(int type) {// cache the returned lists the first timeList<Sensor> list;// 获取所有sensorfinal List<Sensor> fullList = getFullSensorList();synchronized (mSensorListByType) {// 查看mSensorListByType缓存里有没有要组成的sensor,如果有的话直接返回list = mSensorListByType.get(type);if (list == null) {if (type == Sensor.TYPE_ALL) {list = fullList;} else {list = new ArrayList<Sensor>();for (Sensor i : fullList) {// 遍历所有sensor,找到对应type的sensorif (i.getType() == type) {list.add(i);}}}// Collections.unmodifiableList用于构造一个不能修改的Listlist = Collections.unmodifiableList(list);// 存入mSensorListByType缓存mSensorListByType.append(type, list);}}return list;
}
public boolean registerListener(SensorEventListener listener, Sensor sensor,int samplingPeriodUs) {return registerListener(listener, sensor, samplingPeriodUs, null);
}
public boolean registerListener(SensorEventListener listener, Sensor sensor,int samplingPeriodUs, Handler handler) {int delay = getDelay(samplingPeriodUs);return registerListenerImpl(listener, sensor, delay, handler, 0, 0);
}
private static int getDelay(int rate) {int delay = -1;switch (rate) {case SENSOR_DELAY_FASTEST:delay = 0;break;case SENSOR_DELAY_GAME:delay = 20000;break;case SENSOR_DELAY_UI:delay = 66667;break;case SENSOR_DELAY_NORMAL:delay = 200000;break;default:delay = rate;break;}return delay;
}
public void unregisterListener(SensorEventListener listener) {if (listener == null) {return;}unregisterListenerImpl(listener, null);
}

代码路径:frameworks/base/core/java/android/hardware/SystemSensorManager.java,SystemSensorManager继承SensorManager,完成getFullSensorList,registerListenerImpl,unregisterListenerImpl接口。

private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners =new HashMap<SensorEventListener, SensorEventQueue>();protected List<Sensor> getFullSensorList() {return mFullSensorsList;
}
public SystemSensorManager(Context context, Looper mainLooper) {synchronized (sLock) {if (!sNativeClassInited) {sNativeClassInited = true;nativeClassInit();}}mMainLooper = mainLooper;mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion;mContext = context;mNativeInstance = nativeCreate(context.getOpPackageName());// initialize the sensor listfor (int index = 0;; ++index) {Sensor sensor = new Sensor();if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;mFullSensorsList.add(sensor);mHandleToSensor.put(sensor.getHandle(), sensor);}
}
protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor,int delayUs, Handler handler, int maxBatchReportLatencyUs, int reservedFlags) {android.util.SeempLog.record_sensor_rate(381, sensor, delayUs);if (listener == null || sensor == null) {Log.e(TAG, "sensor or listener is null");return false;}// Trigger Sensors should use the requestTriggerSensor call.if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {Log.e(TAG, "Trigger Sensors should use the requestTriggerSensor.");return false;}if (maxBatchReportLatencyUs < 0 || delayUs < 0) {Log.e(TAG, "maxBatchReportLatencyUs and delayUs should be non-negative");return false;}if (mSensorListeners.size() >= MAX_LISTENER_COUNT) {throw new IllegalStateException("register failed, "+ "the sensor listeners size has exceeded the maximum limit "+ MAX_LISTENER_COUNT);}// Invariants to preserve:
// - one Looper per SensorEventListener
// - one Looper per SensorEventQueue
// We map SensorEventListener to a SensorEventQueue, which holds the loopersynchronized (mSensorListeners) {// 从缓存mSensorListeners里获取listenerSensorEventQueue queue = mSensorListeners.get(listener);if (queue == null) {Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;final String fullClassName =listener.getClass().getEnclosingClass() != null? listener.getClass().getEnclosingClass().getName(): listener.getClass().getName();// 新建一个SensorEventQueuequeue = new SensorEventQueue(listener, looper, this, fullClassName);// 添加sensorif (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs)) {queue.dispose();return false;}// 将listener和queue存到缓存mSensorListeners中mSensorListeners.put(listener, queue);return true;} else {return queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs);}}
}/** @hide */
@Override
protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {android.util.SeempLog.record_sensor(382, sensor);// Trigger Sensors should use the cancelTriggerSensor call.if (sensor != null && sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {return;}synchronized (mSensorListeners) {SensorEventQueue queue = mSensorListeners.get(listener);if (queue != null) {boolean result;if (sensor == null) {result = queue.removeAllSensors();} else {result = queue.removeSensor(sensor, true);}if (result && !queue.hasSensors()) {mSensorListeners.remove(listener);queue.dispose();}}}
}

在registerListenerImpl函数里面,若之前该SensorEventListener(由应用自己创建)没有创建过SensorEventQueue,那么创建一个,并和SensorEventListener一起添加到mSensorListeners,当创建SensorEventQueue时,会同时构造一个其父类对象BaseEventQueue

static final class SensorEventQueue extends BaseEventQueue {private final SensorEventListener mListener;private final SparseArray<SensorEvent> mSensorsEvents = new SparseArray<SensorEvent>();public SensorEventQueue(SensorEventListener listener, Looper looper,SystemSensorManager manager, String packageName) {// 调用父类方法super(looper, manager, OPERATING_MODE_NORMAL, packageName);mListener = listener;}......// Called from native code.@SuppressWarnings("unused")@Override// 数据分发,调用onAccuracyChanged,onSensorChangedprotected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,long timestamp) {final Sensor sensor = mManager.mHandleToSensor.get(handle);if (sensor == null) {// sensor disconnectedreturn;}SensorEvent t = null;synchronized (mSensorsEvents) {t = mSensorsEvents.get(handle);}if (t == null) {// This may happen if the client has unregistered and there are pending events in// the queue waiting to be delivered. Ignore.return;}// Copy from the values array.System.arraycopy(values, 0, t.values, 0, t.values.length);t.timestamp = timestamp;t.accuracy = inAccuracy;t.sensor = sensor;// call onAccuracyChanged() only if the value changesfinal int accuracy = mSensorAccuracies.get(handle);if ((t.accuracy >= 0) && (accuracy != t.accuracy)) {mSensorAccuracies.put(handle, t.accuracy);// 回调应用重写的onAccuracyChangedmListener.onAccuracyChanged(t.sensor, t.accuracy);}// 回调应用重写的onSensorChangedmListener.onSensorChanged(t);}

接着在其nativeInitBaseEventQueue()方法里边,通过jni调用android/frameworks/base/core/jni/android_hardware_SensorManager.cpp nativeInitSensorEventQueue()中创建一个接收数据的Receiver对象,这个之后分析数据如何给上来再详细分析。

接着看注册流程,然后,通过BaseEventQueue::addSensor()将注册一个sensor的流程继续往下走,

public boolean addSensor(Sensor sensor, int delayUs, int maxBatchReportLatencyUs) {// Check if already present.int handle = sensor.getHandle();if (mActiveSensors.get(handle)) return false;// Get ready to receive events before calling enable.mActiveSensors.put(handle, true);addSensorEvent(sensor);if (enableSensor(sensor, delayUs, maxBatchReportLatencyUs) != 0) {// Try continuous mode if batching fails.if (maxBatchReportLatencyUs == 0|| maxBatchReportLatencyUs > 0 && enableSensor(sensor, delayUs, 0) != 0) {removeSensor(sensor, false);return false;}}return true;
}

通过BaseEventQueue::enableSensor()继续往下走,

private int enableSensor(Sensor sensor, int rateUs, int maxBatchReportLatencyUs) {if (mNativeSensorEventQueue == 0) throw new NullPointerException();if (sensor == null) throw new NullPointerException();return nativeEnableSensor(mNativeSensorEventQueue, sensor.getHandle(), rateUs,maxBatchReportLatencyUs);
}

通过jni调用C++的接口,android/frameworks/base/core/jni/android_hardware_SensorManager.cpp

static jint nativeEnableSensor(JNIEnv *env, jclass clazz, jlong eventQ, jint handle, jint rate_us,jint maxBatchReportLatency) {sp<Receiver> receiver(reinterpret_cast<Receiver *>(eventQ));return receiver->getSensorEventQueue()->enableSensor(handle, rate_us, maxBatchReportLatency,0);
}

接下来到了 android/frameworks/native/libs/sensor/SensorEventQueue.cpp

status_t SensorEventQueue::enableSensor(int32_t handle, int32_t samplingPeriodUs,int64_t maxBatchReportLatencyUs, int reservedFlags) const {return mSensorEventConnection->enableDisable(handle, true, us2ns(samplingPeriodUs),us2ns(maxBatchReportLatencyUs), reservedFlags);
}

接着就到了 android/frameworks/native/services/sensorservice/SensorEventConnection.cpp

status_t SensorService::SensorEventConnection::enableDisable(int handle, bool enabled, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs,int reservedFlags)
{status_t err;if (enabled) {err = mService->enable(this, handle, samplingPeriodNs, maxBatchReportLatencyNs,reservedFlags, mOpPackageName);} else {err = mService->disable(this, handle);}return err;
}

接下去就是android/frameworks/native/services/sensorservice/SensorService.cpp SensorService::enable()

status_t SensorService::enable(const sp<SensorEventConnection>& connection,int handle, nsecs_t samplingPeriodNs, nsecs_t maxBatchReportLatencyNs, int reservedFlags,const String16& opPackageName) {if (mInitCheck != NO_ERROR)return mInitCheck;sp<SensorInterface> sensor = getSensorInterfaceFromHandle(handle);if (sensor == nullptr ||!canAccessSensor(sensor->getSensor(), "Tried enabling", opPackageName)) {return BAD_VALUE;}ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock);if (mCurrentOperatingMode != NORMAL&& !isWhiteListedPackage(connection->getPackageName())) {return INVALID_OPERATION;}SensorRecord* rec = mActiveSensors.valueFor(handle);if (rec == nullptr) {rec = new SensorRecord(connection);mActiveSensors.add(handle, rec);if (sensor->isVirtual()) {mActiveVirtualSensors.emplace(handle);}// There was no SensorRecord for this sensor which means it was previously disabled. Mark// the recent event as stale to ensure that the previous event is not sent to a client. This// ensures on-change events that were generated during a previous sensor activation are not// erroneously sent to newly connected clients, especially if a second client registers for// an on-change sensor before the first client receives the updated event. Once an updated// event is received, the recent events will be marked as current, and any new clients will// immediately receive the most recent event.if (sensor->getSensor().getReportingMode() == AREPORTING_MODE_ON_CHANGE) {auto logger = mRecentEvent.find(handle);if (logger != mRecentEvent.end()) {logger->second->setLastEventStale();}}} else {if (rec->addConnection(connection)) {// this sensor is already activated, but we are adding a connection that uses it.// Immediately send down the last known value of the requested sensor if it's not a// "continuous" sensor.if (sensor->getSensor().getReportingMode() == AREPORTING_MODE_ON_CHANGE) {// NOTE: The wake_up flag of this event may get set to// WAKE_UP_SENSOR_EVENT_NEEDS_ACK if this is a wake_up event.auto logger = mRecentEvent.find(handle);if (logger != mRecentEvent.end()) {sensors_event_t event;// Verify that the last sensor event was generated from the current activation// of the sensor. If not, it is possible for an on-change sensor to receive a// sensor event that is stale if two clients re-activate the sensor// simultaneously.if(logger->second->populateLastEventIfCurrent(&event)) {event.sensor = handle;if (event.version == sizeof(sensors_event_t)) {if (isWakeUpSensorEvent(event) && !mWakeLockAcquired) {setWakeLockAcquiredLocked(true);}connection->sendEvents(&event, 1, nullptr);if (!connection->needsWakeLock() && mWakeLockAcquired) {checkWakeLockStateLocked(&connLock);}}}}}}}if (connection->addSensor(handle)) {BatteryService::enableSensor(connection->getUid(), handle);// the sensor was added (which means it wasn't already there)// so, see if this connection becomes activemConnectionHolder.addEventConnectionIfNotPresent(connection);} else {ALOGW("sensor %08x already enabled in connection %p (ignoring)",handle, connection.get());}// Check maximum delay for the sensor.nsecs_t maxDelayNs = sensor->getSensor().getMaxDelay() * 1000LL;if (maxDelayNs > 0 && (samplingPeriodNs > maxDelayNs)) {samplingPeriodNs = maxDelayNs;}nsecs_t minDelayNs = sensor->getSensor().getMinDelayNs();if (samplingPeriodNs < minDelayNs) {samplingPeriodNs = minDelayNs;}ALOGD_IF(DEBUG_CONNECTIONS, "Calling batch handle==%d flags=%d""rate=%" PRId64 " timeout== %" PRId64"",handle, reservedFlags, samplingPeriodNs, maxBatchReportLatencyNs);status_t err = sensor->batch(connection.get(), handle, 0, samplingPeriodNs,maxBatchReportLatencyNs);// Call flush() before calling activate() on the sensor. Wait for a first// flush complete event before sending events on this connection. Ignore// one-shot sensors which don't support flush(). Ignore on-change sensors// to maintain the on-change logic (any on-change events except the initial// one should be trigger by a change in value). Also if this sensor isn't// already active, don't call flush().if (err == NO_ERROR &&sensor->getSensor().getReportingMode() == AREPORTING_MODE_CONTINUOUS &&rec->getNumConnections() > 1) {connection->setFirstFlushPending(handle, true);status_t err_flush = sensor->flush(connection.get(), handle);// Flush may return error if the underlying h/w sensor uses an older HAL.if (err_flush == NO_ERROR) {rec->addPendingFlushConnection(connection.get());} else {connection->setFirstFlushPending(handle, false);}}if (err == NO_ERROR) {ALOGD_IF(DEBUG_CONNECTIONS, "Calling activate on %d", handle);err = sensor->activate(connection.get(), true);}if (err == NO_ERROR) {connection->updateLooperRegistration(mLooper);if (sensor->getSensor().getRequiredPermission().size() > 0 &&sensor->getSensor().getRequiredAppOp() >= 0) {connection->mHandleToAppOp[handle] = sensor->getSensor().getRequiredAppOp();}mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex) =SensorRegistrationInfo(handle, connection->getPackageName(),samplingPeriodNs, maxBatchReportLatencyNs, true);mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;}if (err != NO_ERROR) {// batch/activate has failed, reset our state.cleanupWithoutDisableLocked(connection, handle);}return err;
}

接下来看android/frameworks/native/services/sensorservice/SensorInterface.cpp

status_t HardwareSensor::activate(void* ident, bool enabled) {return mSensorDevice.activate(ident, mSensor.getHandle(), enabled);
}

接下来看android/frameworks/native/services/sensorservice/SensorDevice.cpp

status_t SensorDevice::activate(void* ident, int handle, int enabled) {if (mSensors == nullptr) return NO_INIT;Mutex::Autolock _l(mLock);return activateLocked(ident, handle, enabled);
}status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) {bool activateHardware = false;status_t err(NO_ERROR);ssize_t activationIndex = mActivationCount.indexOfKey(handle);if (activationIndex < 0) {ALOGW("Handle %d cannot be found in activation record", handle);return BAD_VALUE;}Info& info(mActivationCount.editValueAt(activationIndex));ALOGD_IF(DEBUG_CONNECTIONS,"SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu",ident, handle, enabled, info.batchParams.size());if (enabled) {ALOGD_IF(DEBUG_CONNECTIONS, "enable index=%zd", info.batchParams.indexOfKey(ident));if (isClientDisabledLocked(ident)) {ALOGE("SensorDevice::activate, isClientDisabledLocked(%p):true, handle:%d",ident, handle);return INVALID_OPERATION;}if (info.batchParams.indexOfKey(ident) >= 0) {if (info.numActiveClients() > 0 && !info.isActive) {activateHardware = true;}} else {// Log error. Every activate call should be preceded by a batch() call.ALOGE("\t >>>ERROR: activate called without batch");}} else {ALOGD_IF(DEBUG_CONNECTIONS, "disable index=%zd", info.batchParams.indexOfKey(ident));// If a connected dynamic sensor is deactivated, remove it from the// dictionary.auto it = mConnectedDynamicSensors.find(handle);if (it != mConnectedDynamicSensors.end()) {delete it->second;mConnectedDynamicSensors.erase(it);}if (info.removeBatchParamsForIdent(ident) >= 0) {if (info.numActiveClients() == 0) {// This is the last connection, we need to de-activate the underlying h/w sensor.activateHardware = true;} else {// Call batch for this sensor with the previously calculated best effort// batch_rate and timeout. One of the apps has unregistered for sensor// events, and the best effort batch parameters might have changed.ALOGD_IF(DEBUG_CONNECTIONS,"\t>>> actuating h/w batch 0x%08x %" PRId64 " %" PRId64, handle,info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch);checkReturn(mSensors->batch(handle, info.bestBatchParams.mTSample, info.bestBatchParams.mTBatch));}} else {// sensor wasn't enabled for this ident}if (isClientDisabledLocked(ident)) {return NO_ERROR;}}if (activateHardware) {err = doActivateHardwareLocked(handle, enabled);if (err != NO_ERROR && enabled) {// Failure when enabling the sensor. Clean up on failure.info.removeBatchParamsForIdent(ident);} else {// Update the isActive flag if there is no error. If there is an error when disabling a// sensor, still set the flag to false since the batch parameters have already been// removed. This ensures that everything remains in-sync.info.isActive = enabled;}}return err;
}
status_t SensorDevice::doActivateHardwareLocked(int handle, bool enabled) {ALOGD_IF(DEBUG_CONNECTIONS, "\t>>> actuating h/w activate handle=%d enabled=%d", handle,enabled);status_t err = checkReturnAndGetStatus(mSensors->activate(handle, enabled));ALOGE_IF(err, "Error %s sensor %d (%s)", enabled ? "activating" : "disabling", handle,strerror(-err));return err;
}

接下来通过hidl,到了HAL,先到android 自己实现的hal层,就称它为android sensor hal吧,后面再往下手机厂家自己实现的hal层称为oem sensor hal 咯,

这边也是直接往下看流程,后面会再分析一下android sensor hal的启动,android/hardware/interfaces/sensors/1.0/default/Sensors.cpp

Return<Result> Sensors::activate(int32_t sensor_handle, bool enabled) {return ResultFromStatus(mSensorDevice->activate(reinterpret_cast<sensors_poll_device_t *>(mSensorDevice),sensor_handle,enabled));
}

接着到了android/hardware/libhardware/modules/sensors/multihal.cpp

static int device__activate(struct sensors_poll_device_t *dev, int handle,int enabled) {sensors_poll_context_t* ctx = (sensors_poll_context_t*) dev;return ctx->activate(handle, enabled);
}
int sensors_poll_context_t::activate(int handle, int enabled) {int retval = -EINVAL;ALOGV("activate");int local_handle = get_local_handle(handle);sensors_poll_device_t* v0 = this->get_v0_device_by_handle(handle);if (halIsCompliant(this, handle) && local_handle >= 0 && v0) {retval = v0->activate(v0, local_handle, enabled);} else {ALOGE("IGNORING activate(enable %d) call to non-API-compliant sensor handle=%d !",enabled, handle);}ALOGV("retval %d", retval);return retval;
}

这边再下去就是厂家自己实现的接口了,android的部分到此为止

1 . Android calls sensors_open()
2 . A sensors hal obiect is created
a.The sensors hal sends lookup requests to the SSC about all known data types.
b.The sensor object is created when an SUID is available for a data type.
i.Sensor attributes are queried and stored in sensor object
ii.A sensor t object is populated using the attributesc . A list of available sensors is populated in sensors hal
3 . Android calls get_sensors_list().
a . A list of available sensors is returned
4 . Android calls batch ()
a . The sensors hal finds a sensor corresponding to the handleb . This sensor is configured with new parameterssensor is active . a new config request is sent
5 . Android calls activatea .
a.The sensors hal finds a sensor corresponding to the handle.
b . If this sensor is activated , a new ssc_connection ( ) is made and config request is sent.
c . If this sensor is deactivated . a ssc_connection is closed
6 . Android calls poll
a. A sensors hal returns available events
7.Android calls flush ()
a.A sensors hal finds a sensor corresponding to the handle
b . Calls sensor.flush ()
i.The sensor sends a flush request to the SSC

Android SensorManager学习相关推荐

  1. java/android 设计模式学习笔记(1)--- 单例模式

    前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使 ...

  2. Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition

    Android Animation学习(五) ApiDemos解析:容器布局动画 LayoutTransition Property animation系统还提供了对ViewGroup中的View改变 ...

  3. android Fragment 学习资料推荐

    为什么80%的码农都做不了架构师?>>>    android   Fragment 学习资料推荐:android大神 郭霖 http://blog.csdn.net/guolin_ ...

  4. android service 学习(上)

    转载自:http://www.cnblogs.com/allin/archive/2010/05/15/1736458.html Service是android 系统中的一种组件,它跟Activity ...

  5. android service 学习(下)

    android service 学习(下) 通常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序UI的方式写个运行在一个不同的进程中的service.在android ...

  6. Android:学习AIDL,这一篇文章就够了(下)

    前言 上一篇博文介绍了关于AIDL是什么,为什么我们需要AIDL,AIDL的语法以及如何使用AIDL等方面的知识,这一篇博文将顺着上一篇的思路往下走,接着介绍关于AIDL的一些更加深入的知识.强烈建议 ...

  7. Android Binder 学习笔记

    前言: Binder是Android给我们提供的一种跨进程通信方式.理解Binder能帮助我们更好的理解Android的系统设计,比如说四大组件,AMS,WMS等系统服务的底层通信机制就都是基于Bin ...

  8. Android画图学习总结(四)——Animation(中)

    在Android画图学习总结(四)--Animation(上)中详细介绍了Tween Animation的定义.使用,由于篇幅有限,很多中重要的方面没有说明,这篇文章一方面做个完整的总结说明,另外一方 ...

  9. android培训内容明细,记录Android开发学习

    记录Android开发学习 Menu菜单学习 1.掌握Android中菜单的创建. 2.掌握Intent信使组件. 创建菜单Menu 我们模仿微信菜单栏学习,创建一个于微信菜单栏相似的菜单 那么我们应 ...

最新文章

  1. 一体化市场谋定国际品牌贸易 对话国际农民丰收节贸易会
  2. 计划策略-25-具有配置的订货型生产
  3. spring创建web项目_使用Spring WS创建合同优先的Web服务
  4. LeetCode 369. 给单链表加一(递归)
  5. 俯首甘为孺子牛上一句是什么
  6. 7台不同配置M1 MacBook Pro真实测试
  7. Javascript特效:关闭小广告
  8. 排球记分员计分程序(三)————设计文档的编写及构架概要设计
  9. 数学建模优化模型简单例题_数学建模案例分析--最优化方法建模7习题六 -
  10. Rust vs. Go:为什么他们在一起更好
  11. Assembler--Error: invalid instruction suffix for `push‘
  12. 【最优化方法】K-Means聚类实验:Python实现手写数字图像MNIST分类
  13. 一个Logo道出了DELL EMC存储的追求与梦想
  14. 移动Web笔记——视口/二倍图/多倍图/背景缩放
  15. 机器学习笔试精选 100 题
  16. 百练4124:海贼王之伟大航路
  17. 快速入门快应用——quickapp构建与发布
  18. java用switch语句算,Java的switch语句与条件运算符
  19. IAR for MSP430 无法破解 的 解决办法
  20. (Python)LeetCode1386:安排电影院座位

热门文章

  1. python声明变量
  2. MySQL MyCAT 读写分离实战
  3. java怎么把把数组元素倒置_java数组元素倒置
  4. java web面试题
  5. Excel重设格式并自动截图保存为图片
  6. javaScript中超过int的最大范围
  7. GIS(地理信息系统/地理信息科学)职称评审二:中科院和人社部职称评审所需材料内容对比
  8. qml实现的聊天界面
  9. 经典递归算法——汉诺塔问题
  10. C++输出数据为.csv格式