Android GPS学习笔记(三)定位数据如何从GPS芯片到应用层
定位的基础知识:
1、定位芯片和CPU之间通过串口进行通信
2、串口和CPU之间传输的是ASCII格式的NMEA(National Marine Electronics Association)信息,如:
- $GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F
- $GPGLL,4250.5589,S,14718.5084,E,092204.999,A*2D
- $GPGSV,3,1,10,20,78,331,45,01,59,235,47,22,41,069,,13,32,252,45*70
- $GPRMC,092204.999,A,4250.5589,S,14718.5084,E,0.00,89.68,211200,,*25
基于以上两点,要探知定位数据从GPS芯片到应用层的流程,最好的途径就是从应用层输出NEMA信息的地方开始。
NMEA资料参见:卫星定位数据NMEA介绍
一、GPS定位的应用层实现
Luckily,在应用层我们可以通过onNmeaReceived()方法获取到NMEA信息,如下Code Fragment:
- public class GpsTestActivity extends ActionBarActivity {
- /* Other Codes */
- /** 获取系统的定位服务,记得在AndroidManifest中赋予定位方面的权限:
- * <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
- * <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
- * <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
- */
- LocationManager mLocationService = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
- mLocationService.addNmeaListener(mNmeaListener);
- private GpsStatus.NmeaListener mNmeaListener = new NmeaListener() {
- @Override
- public void onNmeaReceived(long timestamp, String nmea) {
- System.out.println(nmea + "\n");
- }
- };
- }
二、GPS定位的Framework层实现
GpsStatus.NmeaListener是一个接口类,来自GpsStatus.java文件:
- frameworks\base\location\java\android\location\GpsStatus.java
- /**
- * Used for receiving NMEA sentences from the GPS.
- * NMEA 0183 is a standard for communicating with marine electronic devices
- * and is a common method for receiving data from a GPS, typically over a serial port.
- * See <a href="http://en.wikipedia.org/wiki/NMEA_0183">NMEA 0183</a> for more details.
- * You can implement this interface and call {@link LocationManager#addNmeaListener}
- * to receive NMEA data from the GPS engine.
- */
- public interface NmeaListener {
- void onNmeaReceived(long timestamp, String nmea);
- }
在上述App中,我们的应用程序实现了该方法,一旦NMEA数据到来,onNmeaReceived()方法就被调用一次,我们在Console上可以看到原始的NEMA信息。
那么接下来,就要寻找nmea数据的来源了。
mNmeaListener通过LocationManager类的addNmeaListener()方法进行注册(register):
- frameworks\base\location\java\android\location\LocationManager.java
- /**
- * Adds an NMEA listener.
- *
- * @param listener a {@link GpsStatus.NmeaListener} object to register
- *
- * @return true if the listener was successfully added
- *
- * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
- */
- public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
- boolean result;
- /* mNmeaListeners是LocationManager类的成员变量:
- * private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
- * new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>();
- */
- if (mNmeaListeners.get(listener) != null) {
- // listener is already registered
- return true;
- }
- try {
- GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
- result = mService.addGpsStatusListener(transport);
- if (result) {
- mNmeaListeners.put(listener, transport);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
- result = false;
- }
- return result;
- }
这里,先检测定义的NmeaListener有没有被注册过,若果没有,注册之。
注册到哪里去了呢?
由mNmeaListeners成员的定义可知,和GpsStatus.NmeaListener进行关联的是GpsStatusListenerTransport,而它是LocationManager类的一个内部类。
只看相关的部分:
- // This class is used to send GPS status events to the client's main thread.
- private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
- private final GpsStatus.NmeaListener mNmeaListener;
- // This must not equal any of the GpsStatus event IDs
- private static final int NMEA_RECEIVED = 1000;
- private class Nmea {
- long mTimestamp;
- String mNmea;
- Nmea(long timestamp, String nmea) {
- mTimestamp = timestamp;
- mNmea = nmea;
- }
- }
- private ArrayList<Nmea> mNmeaBuffer;
- //G psStatusListenerTransport(GpsStatus.Listener listener){}
- GpsStatusListenerTransport(GpsStatus.NmeaListener listener) {
- mNmeaListener = listener;
- mListener = null;
- mNmeaBuffer = new ArrayList<Nmea>();
- }
- @Override
- public void onNmeaReceived(long timestamp, String nmea) {
- if (mNmeaListener != null) {
- synchronized (mNmeaBuffer) {
- mNmeaBuffer.add(new Nmea(timestamp, nmea));
- }
- Message msg = Message.obtain();
- msg.what = NMEA_RECEIVED;
- // remove any NMEA_RECEIVED messages already in the queue
- mGpsHandler.removeMessages(NMEA_RECEIVED);
- mGpsHandler.sendMessage(msg);
- }
- }
- private final Handler mGpsHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == NMEA_RECEIVED) {
- synchronized (mNmeaBuffer) {
- int length = mNmeaBuffer.size();
- for (int i = 0; i < length; i++) {
- Nmea nmea = mNmeaBuffer.get(i);
- mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
- }
- mNmeaBuffer.clear();
- }
- } else {
- // synchronize on mGpsStatus to ensure the data is copied atomically.
- }
- }
- }
- };
- }
在GpsStatusListenerTransport类中:
定义一个Nmea类型的链表mNmeaBuffer,一旦onNmeaReceived()接收到NMEA数据,新数据被加载到链表mNmeaBuffer中(mNmeaBuffer.add(new Nmea(timestamp, nmea))),然手置消息标志为NMEA_RECEIVED(msg.what = NMEA_RECEIVED)。
mGpsHandler对上述NMEA_RECEIVED消息进行处理,最终把传过来的NMEA数据发往应用层GpsTestActivity中的onNmeaReceived()。
那么,GpsStatusListenerTransport类中onNmeaReceived(long timestamp, String nmea)方法的nmea数据有谁提供呢?
GpsStatusListenerTransport类继承自IGpsStatusListener,由类前的字符"I"我们得知,它是一个扩展名为.aidl的文件。
注:
AIDL:AIDL机制用来完成在进程之间进行通信(在Android中不允许进程间共享数据),它的详细知识另外Google之。
这里,我们再次见到了onNmeaReceived():
- rameworks\base\location\java\android\location\IGpsStatusListener.aidl
- oneway interface IGpsStatusListener
- {
- void onGpsStarted();
- void onGpsStopped();
- void onFirstFix(int ttff);
- void onSvStatusChanged(int svCount, in int[] prns, in float[] snrs, in float[] elevations, in float[] azimuths, int ephemerisMask, int almanacMask, int usedInFixMask);
- void onNmeaReceived(long timestamp, String nmea);
- }
注 :
oneway关键字是用来修饰远程调用行为。使用该关键词时,远程调用不是阻塞的,它只是发送事物数据并立即返回。接口的最终实现是把普通的远程调用按照Binder线程池的调用规则来接收,如果oneway是使用在本地调用上,那么不会有任何影响,并且调用依然是异步的。
下面,探究必须进入第三层。
三、GPS定位的Lib层实现
和IGpsStatusListener接头的是GpsLocationProvider类:
- frameworks\base\services\java\com\android\server\location\GpsLocationProvider.java
- public class GpsLocationProvider implements LocationProviderInterface {
- // 此处省略1000+N行
- private ArrayList<Listener> mListeners = new ArrayList<Listener>();
- private final class Listener implements IBinder.DeathRecipient {
- final IGpsStatusListener mListener;
- Listener(IGpsStatusListener listener) {
- mListener = listener;
- }
- @Override
- public void binderDied() {
- if (DEBUG) Log.d(TAG, "GPS status listener died");
- synchronized (mListeners) {
- mListeners.remove(this);
- }
- if (mListener != null) {
- mListener.asBinder().unlinkToDeath(this, 0);
- }
- }
- }
- /**
- * called from native code to report NMEA data received
- */
- private void reportNmea(long timestamp) {
- synchronized (mListeners) {
- int size = mListeners.size();
- if (size > 0) {
- // don't bother creating the String if we have no listeners
- int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
- String nmea = new String(mNmeaBuffer, 0, length);
- for (int i = 0; i < size; i++) {
- Listener listener = mListeners.get(i);
- try {
- listener.mListener.onNmeaReceived(timestamp, nmea);
- } catch (RemoteException e) {
- Log.w(TAG, "RemoteException in reportNmea");
- mListeners.remove(listener);
- // adjust for size of list changing
- size--;
- }
- }
- }
- }
- }
- }
GPS定位功能最终需要调用硬件实现,操作硬件就必须通过C/C++完成,GpsLocationProvider中包含许多native方法,采用JNI机制为上层提供服务。
在上面的Code Frame中,通过调用本地方法native_read_nmea()获取到NMEA数据,然后传数据到IGpsStatusListener接口类的onNmeaReceived()方法。
reportNmea()是被JNI方法回调的方法,在 JNI 的实现中,通过这些方法的回调来传递JNI层的执行结果。
源码编译出错,解决问题去。。。
native_read_nmea()在GpsLocationProvider类中定义:
- private native int native_read_nmea(byte[] buffer, int bufferSize);
native指明它是本地方法,和它对应的C/C++文件的实现是:
- static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj, jbyteArray nmeaArray, jint buffer_size);
How?Next...
- frameworks\base\services\jni\com_android_server_location_GpsLocationProvider.cpp
- static JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- /* other members... */
- {"native_read_nmea", "([BI)I", (void*)android_location_GpsLocationProvider_read_nmea},
- /* other members... */
- };
JNINativeMethod是Android中采用的Java和C/C++函数的映射方式,并在其中描述了函数的参数和返回值:
- typedef struct {
- const char* name; // Java文件中的本地方法
- const char* signature; // 述了函数的参数和返回值
- void* fnPtr; // 指针,指向具体的C/C++函数
- } JNINativeMethod;
详细内容这里还是不展开了。
来看android_location_GpsLocationProvider_read_nmea()的实现:
- static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj,
- jbyteArray nmeaArray, jint buffer_size)
- {
- // this should only be called from within a call to reportNmea
- jbyte* nmea = (jbyte *)env->GetPrimitiveArrayCritical(nmeaArray, 0);
- int length = sNmeaStringLength;
- if (length > buffer_size)
- length = buffer_size;
- memcpy(nmea, sNmeaString, length);
- env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT);
- return length;
- }
虽然不清楚JNI深入含义,但这个函数意思还是挺明显的,我们推断:
第5行:用来动态分配内存,nmea指向获取到的内存区域,同时把nmea和nmeaArray进行关联;
第6行:sNmeaStringLength指示一次从串口读取到的字节长度
第7、8行:在Java中调用native_read_nmea()方法时指明了我们需要取的数据长度,所以,如果从串口实际读取的数据长度大于我们需要的,我们对串口数据进行截取:即,只取指定长度的数据;
第9行:从串口读出的数据存在sNmeaString中,这里Copy到nmea指向的内存区域;
第10行:nmea指向的内存区域中的数据交给nmeaArray,然后释放nmea指向的内存空间。这里也可以看到,函数调用是通过nmeaArray传递NMEA数据的
下面应该看sNmeaStringLength、sNmeaString的设置过程:
- static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length)
- {
- JNIEnv* env = AndroidRuntime::getJNIEnv();
- // The Java code will call back to read these values
- // We do this to avoid creating unnecessary String objects
- sNmeaString = nmea;
- sNmeaStringLength = length;
- env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- }
method_reportNmea、、、有没有熟悉的感觉?
对,在GpsLocationProvider类中见过reportNmea(long timestamp)函数。
下面的代码片段表明,method_reportNmea()和reportNmea()是绑定在一起的,调用C/C++函数method_reportNmea,也就间接调用Java的reportNmea()方法。这中间的机制,就是JNI!
- static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
- /* other definitions... */
- method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
- /* other definitions... */
- }
而method_reportNmea是在nmea_callback()函数中被调用的,哪里又调用nmea_callback()函数呢?
Let's go to neXt Layer...
四、GPS定位HAL层的实现
所谓Android的HAL层,也就是是Linux的应用程序。至于串口具体配置,比如寄存器配置、数据收发等芯片级实现,是在在Linux内核里的。
com_android_server_location_GpsLocationProvider.cpp文件中另外出现nmea_callback的地方是:
- GpsCallbacks sGpsCallbacks = {
- sizeof(GpsCallbacks),
- location_callback,
- status_callback,
- sv_status_callback,
- nmea_callback,
- set_capabilities_callback,
- acquire_wakelock_callback,
- release_wakelock_callback,
- create_thread_callback,
- request_utc_time_callback,
- };
GpsCallbacks结构体封装了所有需要回调的函数( 确切的说是函数指针 ),sGpsCallbacks调用关系:
- static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
- {
- // this must be set before calling into the HAL library
- if (!mCallbacksObj)
- mCallbacksObj = env->NewGlobalRef(obj);
- // fail if the main interface fails to initialize
- if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)
- return false;
- /* other codes */
- return true;
- }
而android_location_GpsLocationProvider_init()在GpsLocationProvider类中调用native_init()时被调用:
- static JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init}
- }
- 这里,我们找到了和上层的关系,和下层如何打交道呢?
- 下面需要贴一大段代码:
- /** Represents the standard GPS interface. */
- typedef struct {
- /** set to sizeof(GpsInterface) */
- size_t size;
- /**
- * Opens the interface and provides the callback routines
- * to the implemenation of this interface.
- */
- int (*init)( GpsCallbacks* callbacks );
- /** Starts navigating. */
- int (*start)( void );
- /** Stops navigating. */
- int (*stop)( void );
- /** Closes the interface. */
- void (*cleanup)( void );
- /** Injects the current time. */
- int (*inject_time)(GpsUtcTime time, int64_t timeReference,
- int uncertainty);
- /** Injects current location from another location provider
- * (typically cell ID).
- * latitude and longitude are measured in degrees
- * expected accuracy is measured in meters
- */
- int (*inject_location)(double latitude, double longitude, float accuracy);
- /**
- * Specifies that the next call to start will not use the
- * information defined in the flags. GPS_DELETE_ALL is passed for
- * a cold start.
- */
- void (*delete_aiding_data)(GpsAidingData flags);
- /**
- * min_interval represents the time between fixes in milliseconds.
- * preferred_accuracy represents the requested fix accuracy in meters.
- * preferred_time represents the requested time to first fix in milliseconds.
- */
- int (*set_position_mode)(GpsPositionMode mode, GpsPositionRecurrence recurrence,
- uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time);
- /** Get a pointer to extension information. */
- const void* (*get_extension)(const char* name);
- } GpsInterface;
GpsInterface结构体封装了GPS实现的标准接口——接口,注意!接口不就时用来连接两端的吗?一端是com_android_server_location_GpsLocationProvider.cpp文件里的实现,那另一端就是。。。都探到这个地步了,另一端应该是串口方式直接和GPS芯片打交道的Linux驱动了吧?
确是,但是还需要一个媒介:
- struct gps_device_t {
- struct hw_device_t common;
- /**
- * Set the provided lights to the provided values.
- *
- * Returns: 0 on succes, error code on failure.
- */
- const GpsInterface* (*get_gps_interface)(struct gps_device_t* dev);
- };
然后,
- static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
- int err;
- hw_module_t* module;
- /* other codes..*/
- err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
- if (err == 0) {
- hw_device_t* device;
- err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device);
- if (err == 0) {
- gps_device_t* gps_device = (gps_device_t *)device;
- sGpsInterface = gps_device->get_gps_interface(gps_device);
- }
- }
- /* other codes..*/
- }
- static JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
- }
GpsLocationProvider.java通过class_init_native的调用实现对C/C++文件中android_location_GpsLocationProvider_class_init_native的调用;
com_android_server_location_GpsLocationProvider.cpp通过gps_device_t获取操作GPS芯片的接口。How????
重点来了:GPS_HARDWARE_MODULE_ID
对,就是 GPS_HARDWARE_MODULE_ID !
往下看:
- ardware\qcom\gps\loc_api\libloc_api\gps.c
- struct hw_module_t HAL_MODULE_INFO_SYM = {
- .tag = HARDWARE_MODULE_TAG,
- .version_major = 1,
- .version_minor = 0,
- .id = GPS_HARDWARE_MODULE_ID,
- .name = "loc_api GPS Module",
- .author = "Qualcomm USA, Inc.",
- .methods = &gps_module_methods,
- };
有木有?GPS_HARDWARE_MODULE_ID!
- hardware\qcom\gps\loc_api\libloc_api\gps.c
- extern const GpsInterface* gps_get_hardware_interface();
- const GpsInterface* gps__get_gps_interface(struct gps_device_t* dev)
- {
- return gps_get_hardware_interface();
- }
- static int open_gps(const struct hw_module_t* module, char const* name,
- struct hw_device_t** device)
- {
- struct gps_device_t *dev = malloc(sizeof(struct gps_device_t));
- memset(dev, 0, sizeof(*dev));
- dev->common.tag = HARDWARE_DEVICE_TAG;
- dev->common.version = 0;
- dev->common.module = (struct hw_module_t*)module;
- dev->get_gps_interface = gps__get_gps_interface;
- *device = (struct hw_device_t*)dev;
- return 0;
- }
- static struct hw_module_methods_t gps_module_methods = {
- .open = open_gps
- };
流程很清楚了:
gps_get_hardware_interface()函数在驱动程序中实现
——在gps__get_gps_interface()中被调用
——在open_gps()被调用
——在gps_module_methods中例化
——HAL_MODULE_INFO_SYM
const GpsInterface* gps_get_hardware_interface()函数在其他C文件实现,该C文件是和Linux驱动打交道的应用程序。基本功能:
1、open处理器CPU和GPS芯片连接的串口;
2、read串口NEMA数据,并解析;
3、根据上层传进来的回调函数,打包数据,调用相应Callback,进而发送到Android应用层。
- static const GpsInterface mGpsInterface = {
- .size =sizeof(GpsInterface),
- .init = gps_init,
- |--1、接收从上层传下来的GpsCallbacks变量,用它初始化GpsState->callbacks成员
- |--2、GpsState结构体的其他成员初始化
- |--3、GpsState->init状态设置为:STATE_INIT
- |--4、最重要:启动GPS线程,进行数据的读取、处理:
- state->thread = state->callbacks.create_thread_cb("gps", gps_state_thread, state);
- --gps_create_thread create_thread_cb;
- --typedef pthread_t (* gps_create_thread)(const char* name, void (*start)(void *), void* arg);
- .start = gps_start,
- --设置GPS的状态为开始:GPS_STATUS_SESSION_BEGIN
- .stop = gps_stop,
- --设置GPS的状态为结束:GPS_STATUS_SESSION_END
- .cleanup = gps_cleanup,
- --退出需要进行的一些清理工作,如GpsState->init = STATE_QUIT,GpsCallbacks指针归null,信号量回收
- .inject_time = gps_inject_time,
- --可为空函数
- .inject_location = gps_inject_location,
- --可为空函数
- .delete_aiding_data = gps_delete_aiding_data,
- --可为空函数
- .set_position_mode = gps_set_position_mode,
- --设置GPS工作模式:单GPS、单BD、GPS/BD双系统
- .get_extension = gps_get_extension,
- --定位之外的扩展功能实现
- };
- state->thread = state->callbacks.create_thread_cb("gps", gps_state_thread, state);
- --static void gps_state_thread(void* arg):
- 1、state通过arg参数传入函数
- 2、创建了Time和Nmea数据处理两个线程
- state->nmea_thread = state->callbacks.create_thread_cb("nmea_thread", gps_nmea_thread, state);
- --static void gps_nmea_thread(void* arg)
- --gps_opentty(state);
- nmea_reader_init(reader);
- --nmea_reader_parse(NmeaReader* r) {
- if (gps_state->callbacks.nmea_cb) {
- struct timeval tv;
- unsigned long long mytimems;
- gettimeofday(&tv,NULL);
- mytimems = tv.tv_sec * 1000 + tv.tv_usec / 1000;
- gps_state->callbacks.nmea_cb(mytimems, r->in, r->pos);
- D("reader_parse. %.*s ", r->pos, r->in );
- }
- }
我们是从APP层NMEA信息输出自定向下分析的,APP层信息输出的最终起始是:gps_state->callbacks.nmea_cb(mytimems, r->in, r->pos);
到这里还有个问题:GPS芯片和CPU连接,使用的是哪个串口?这个串口号怎么确定的呢?
打算贴个完整HAL层的实例,考虑到代码很多,下篇在说吧。。
Android GPS学习笔记(三)定位数据如何从GPS芯片到应用层相关推荐
- 【Android架构GPS篇】之定位数据如何从GPS芯片到应用层
原址:http://blog.csdn.net/u013686019/article/details/47444839 写在前面 在漫长的Android源码编译等待过程中,想起之前写过一部分的Andr ...
- Android View学习笔记(三):Scroller的原理剖析及使用(上)
一.前言 上一篇文章中,讨论了View的几种基本滑动方式,但是这些滑动方式是生硬的,在一瞬间完成的,这给用户非常不好的体验,所以为了提高用户体验,我们需要将View弹性滑动.什么是弹性滑动?就是一个V ...
- Android Studio --- [学习笔记]RadioButton、CheckBox、ImageView、ListView、TCP的三次握手
说明 源代码 在2.x里有TCP的三次挥手与四次握手,先对它进行简单的回答(百度).预计在下一篇里,会继续说明TCP 接上一篇: Android Studio - > [学习笔记]Button. ...
- 大数据HiveSQL学习笔记三-查询基础语法以及常用函数
大数据HiveSQL学习笔记三-查询基础语法以及常用函数 一.基础语法 1.SELECT -列名- FROM -表名- WHERE -筛选条件- 如:需要根据城市,性别找出匹配的10个用户 user_ ...
- oracle数据库开多线程,学习笔记:Oracle表数据导入 DBA常用单线程插入 多线程插入 sql loader三种表数据导入案例...
天萃荷净 oracle之数据导入,汇总开发DBA在向表中导入大量数据的案例,如:单线程向数据库中插入数据,多线程向数据表中插入数据,使用sql loader数据表中导入数据案例 1.Oracle数据库 ...
- Android:日常学习笔记(6)——探究活动(3)
Android:日常学习笔记(6)--探究活动(3) 活动的生命周期 返回栈 Android中的活动是可以叠加的,我们每启动一个新活动,就会覆盖在原来的活动上,点击Back以后销毁最上面的活动,下面的 ...
- Android Studio --- [学习笔记]TCP(第2弹)、GridView、ScrollView
说明 这篇主要接上一篇Android Studio - > [学习笔记]RadioButton.CheckBox.ImageView.ListView.TCP的三次握手 对上面回答的细解,并用J ...
- Android 开发学习笔记:七大知识点板块汇总
前言 我从事 Android 开发行业也有些年头,工作期间也接触过很多 Android 开发者, 因此也非常清楚 程序员最大的限制并非年龄而是实力: 但大多数初中级Android工程师,想要提升技能, ...
- 安卓开发Android studio学习笔记12:读取解析XML(案例演示)
Android studio学习笔记 第一步:配置Student.XML 第二步:配置activity_main.xml 第三步:配置student.xml 第四步:配置Student用户类 第五步: ...
- SurfaceFlinger学习笔记(三)之SurfaceFlinger进程
概述 本系列是基于android Q 即android10 SurfaceFlinger学习笔记(一)应用启动流程 SurfaceFlinger学习笔记(二)之Surface SurfaceFling ...
最新文章
- MySQL IN、Exist关联查询时,我们为什么建议小表驱动大表?
- codeblocks如何watch数组
- 原相机怎么拍出网图_专访5位时尚生活达人,他们都用哪款相机记录生活美好瞬间...
- Alibaba Cloud Toolkit——简介
- CF452F Permutations/Luogu2757 等差子序列 树状数组、Hash
- 分析redis中大key的几种办法
- nano-pc-t1 4412 显示驱动分析
- 1218 鼠标样式 cursor
- PaaS的发展将释放物联网开发效率 ——基于云架构的物联网云平台解决方案
- 视频编解码(十三):list_for_each_entry列表总结
- 系统学习深度学习(三十五)--策略梯度(Policy Gradient)
- 目标跟踪学习笔记_2(particle filter初探1)
- 产品线 产品宽度 产品的深度 产品的相关度(理解工厂模式)
- 不可不知的量化因子模型选股策略
- 微信小程序开发动感十足的加载动画--都在这里!
- 橙光游戏c语言代码,橙光游戏一
- 蓝桥杯:排列字母(C++)
- TCP/IP层次模型
- 解决BUG:Incorrect string value: ‘\xAC\xED\x00\x05~r...‘ for column ‘XX‘ at row 1 Query
- 数据结构学习笔记(5.树与二叉树 6.图)
热门文章
- 河北工业机器人夹爪生产厂家_GIMATIC,GIMATIC电动夹爪,GIMATIC气动夹爪-工业控制领域一站式服务商-华联欧...
- matlab中datax,菜鸟学飞--matlab系列1
- xcode怎样配置GLUT和GLTools
- LVSDR模式+keepalived
- 软件工程导论张海蕃书籍pdf_软件工程导论张海蕃 课后习题答案
- 基于vue手写一个分屏器,通过鼠标控制屏幕宽度。
- s3c6410_地址映射
- java 双击触发事件_java鼠标双击事件怎么实现
- 阿里飞冰使用Link路由跳转报错之“react-router”与“react-router-dom”
- 机械大专生能学会云计算吗,完全零基础的