MTK平台Android Gsensor数据校准与数据获取

简介: http://blog.csdn.net/morixinguan/article/details/76850600上节,写WIFI MAC地址的时候我们已经知道,MTKAndroid系统的Gsensor校准的数据其实也是存储在NVRAM中的,Gsensor隶属于传感器系统架构。

http://blog.csdn.net/morixinguan/article/details/76850600

上节,写WIFI MAC地址的时候我们已经知道,MTKAndroid系统的Gsensor校准的数据其实也是存储在NVRAM中的,Gsensor隶属于传感器系统架构。

 


接下来我们来看下Gsensor校准的基准图像:




那么如何来校准Gsensor的X,Y,Z三个方向呢?我们可以参考MTK提供的工厂测试factroymode的代码:

位置在:vendor\mediatek\proprietary\factory\src\test\ftm_gs_cali.c  ftm_gsensor.c

ftm_gs_cali.c就是校准的源码,我们可以打开来看看它具体的实现原理:

在ftm_gs_cali.c的static void*gs_cali_update_iv_thread(void *priv)这个函数中,我们可以看到如何校准Gsensor数值的过程:

static void *gs_cali_update_iv_thread(void *priv)
{
	struct gsc_data *dat = (struct gsc_data *)priv; 
	struct gsc_priv *gsc = &dat->gsc;
	struct itemview *iv = dat->iv;
	int err = 0, len = 0;
	char *status;
	HwmData cali;
	static int op = -1;
	int  max_retry = 3, retry_period = 100, retry=0;
    unsigned int flags = 1;

	LOGD(TAG "%s: Start\n", __FUNCTION__);
	//打开gsensor
	err = gsensor_open(&(gsc->fd));
	if(err)
	{
		memset(dat->info, 0x00, sizeof(dat->info));
		sprintf(dat->info, uistr_info_sensor_init_fail);
		iv->redraw(iv);
		GSCLOGE("gs_cali_open() err = %d(%s)\n", err, dat->info);
		pthread_exit(NULL);
		return NULL;
	}
/** Enable G-sensor **/
//使能Gsensor,让它开始工作
    while ((err = ioctl(gsc->fd, GSENSOR_IOCTL_INIT, &flags)) && (retry ++ < max_retry))
    usleep(retry_period*1000);
    if (err) {
        LOGD("enable g-sensor fail: %s", strerror(errno));
        return -1;
	}

	while(1)
	{

		if(dat->exit_thd)
		{
			break;
		}

		pthread_mutex_lock(&dat->gsc.evtmutex);
		if(op != dat->gsc.pending_op)
		{
			op = dat->gsc.pending_op;
			GSCLOGD("op: %d\n", dat->gsc.pending_op);
		}
		pthread_mutex_unlock(&dat->gsc.evtmutex);
		err = 0;

		if(op == GS_OP_CLEAR)
		{
			memset(&dat->gsc.cali_nvram, 0x00, sizeof(dat->gsc.cali_nvram));
			memset(&dat->gsc.cali_drv, 0x00, sizeof(dat->gsc.cali_drv));
			err = gsensor_rst_cali(gsc->fd);
			if(err)
			{
				GSCLOGE("rst calibration: %d\n", err);                
			}
			else if((err = gsensor_write_nvram(&dat->gsc.cali_nvram)) != 0)
			{
				GSCLOGE("write nvram: %d\n", err);                
			}

			if(err)
			{
				snprintf(dat->gsc.status, sizeof(dat->gsc.status), uistr_info_sensor_cali_fail);
				//dat->mod->test_result = FTM_TEST_FAIL;
			}
			else
			{
				snprintf(dat->gsc.status, sizeof(dat->gsc.status), uistr_info_sensor_cali_ok);
				//dat->mod->test_result = FTM_TEST_PASS;
			}

			gsc->bUpToDate = false;    
			pthread_mutex_lock(&dat->gsc.evtmutex);
			dat->gsc.pending_op = GS_OP_NONE;
			pthread_mutex_unlock(&dat->gsc.evtmutex);
		}
		else if(op == GS_OP_CALI_PRE)
		{
			err = 0;
			/*by-pass*/
			snprintf(dat->gsc.status, sizeof(dat->gsc.status), uistr_info_sensor_cali_ongoing);            
			pthread_mutex_lock(&dat->gsc.evtmutex);
			dat->gsc.pending_op = GS_OP_CALI;
			pthread_mutex_unlock(&dat->gsc.evtmutex);
		}
		else if(op == GS_OP_CALI)
		{
			if(!dat->gsc.cali_delay || !dat->gsc.cali_num || !dat->gsc.cali_tolerance)
			{
				GSCLOGE("ignore calibration: %d %d %d\n", dat->gsc.cali_delay, dat->gsc.cali_num, dat->gsc.cali_tolerance);                
			}
			//执行校准的动作
			else if((err = gsensor_calibration(gsc->fd, dat->gsc.cali_delay, dat->gsc.cali_num, 
			                          dat->gsc.cali_tolerance, 0, &cali)) != 0)
			{
				GSCLOGE("calibrate acc: %d\n", err);                
			}
			//设置校准cali,让校准的数据开始生效
			else if((err = gsensor_set_cali(gsc->fd, &cali)) != 0)
			{    
				GSCLOGE("set calibration fail: (%s) %d\n", strerror(errno), err);
			}
			else if((err = gsensor_get_cali(gsc->fd, &cali)) != 0)
			{    
				GSCLOGE("get calibration fail: (%s) %d\n", strerror(errno), err);
			}
			//将校准的数据写入到nvram中去
			else if((err = gsensor_write_nvram(&cali)) != 0)
			{
				GSCLOGE("write nvram fail: (%s) %d\n", strerror(errno), err);
			}
			else
			{
				dat->gsc.cali_delay = dat->gsc.cali_num = dat->gsc.cali_tolerance = 0;
				dat->gsc.bUpToDate = false;
			}
			
			if(err)
			{
				len = snprintf(dat->gsc.status, sizeof(dat->gsc.status), uistr_info_sensor_cali_fail);  
				dat->mod->test_result = FTM_TEST_FAIL;
			}
			else
			{
				len = snprintf(dat->gsc.status, sizeof(dat->gsc.status), uistr_info_sensor_cali_ok);
				dat->mod->test_result = FTM_TEST_PASS;
			}

			pthread_mutex_lock(&dat->gsc.evtmutex);
			dat->gsc.pending_op = GS_OP_NONE;
			pthread_mutex_unlock(&dat->gsc.evtmutex);
		}

		err = gs_cali_update_info(gsc);
		if(err)
		{
			GSCLOGE("gs_cali_update_info() = (%s), %d\n", strerror(errno), err);
			break;
		} 


		len = 0;
		len += snprintf(dat->info+len, sizeof(dat->info)-len, "R: %+7.4f %+7.4f %+7.4f\n", gsc->dat.x, gsc->dat.y, gsc->dat.z);
		len += snprintf(dat->info+len, sizeof(dat->info)-len, "D: %+7.4f %+7.4f %+7.4f\n", gsc->cali_drv.x, gsc->cali_drv.y, gsc->cali_drv.z);
		len += snprintf(dat->info+len, sizeof(dat->info)-len, "N: %+7.4f %+7.4f %+7.4f\n", gsc->cali_nvram.x, gsc->cali_nvram.y, gsc->cali_nvram.z);
		len += snprintf(dat->info+len, sizeof(dat->info)-len, "%s\n", gsc->status);
		        
		iv->set_text(iv, &dat->text);
		iv->redraw(iv);
	}
	//关闭gsensor
	gs_cali_close(gsc);
	LOGD(TAG "%s: Exit\n", __FUNCTION__);    
	pthread_exit(NULL);

	return NULL;
}

这段代码虽然很多,但我们可以找出校准Gsensor的核心流程主要如下:

(1)    打开Gsensor

(2)    使能Gsensor

(3)    执行校准的动作

(4)    设置校准的Cail,让校准的数据生效

(5)    将校准得到的数据写入到nvram中

(6)    关闭Gsensor

核心流程我们已经清楚了,那么接下来如何来写这个程序呢?我们要弄明白,这些函数上哪个文件里去找这是第一步:

通过grep命令搜索相关函数,最终确定,这些函数的头文件在:

./pskyed/libs/em_emmc_comm/libhwm/include/libhwm.h这里,在这个头文件中有相关的函数可以给我们使用:

extern int gsensor_calibration(int fd, int period, int count, int tolerance, int trace, HwmData *cali);
extern int gsensor_write_nvram(HwmData *dat);
extern int gsensor_read_nvram(HwmData *dat);
extern int gsensor_rst_cali(int fd);
extern int gsensor_set_cali(int fd, HwmData *dat);
extern int gsensor_get_cali(int fd, HwmData *dat);
extern int gsensor_read(int fd, HwmData *dat);
extern int gsensor_init(int fd);
extern int gsensor_close(int fd);
extern int gsensor_open(int *fd);
extern int gyroscope_calibration(int fd, int period, int count, int tolerance, int trace, HwmData *cali);
extern int gyroscope_write_nvram(HwmData *dat);
extern int gyroscope_read_nvram(HwmData *dat);
extern int gyroscope_rst_cali(int fd);
extern int gyroscope_set_cali(int fd, HwmData *dat);
extern int gyroscope_get_cali(int fd, HwmData *dat);
extern int gyroscope_read(int fd, HwmData *dat);
extern int gyroscope_close(int fd);
extern int gyroscope_open(int *fd);
extern int gyroscope_init(int fd);

那么这些函数的源码在哪里呢?源码是没有的,因为MTK厂商将这部分代码给封装成了so动态库文件,所以,我们需要找到这个头文件对应的so文件,这样我们才能使用这个头文件,调用到so动态库中的函数。

通过搜索得知,这个so动态库文件在以下路径:

./pskyed/libs/em_emmc_comm/libhwm/libhwm.so

知道这些以后,下面我们就可以写一个简单的程序来验证这个过程了,这个留给读者自己去测试,接口我已经写好了,我的项目源码不便于公开,请读者自己拿去修改验证,流程是一样的,接口没有改过,至于想实现什么样的效果请读者自己去尝试添加,移植我的程序进行修改。

下面实现这个校准程序:

gs_cali.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/mount.h>
#include <sys/statfs.h>
#include <sys/reboot.h>
#include <dirent.h>
#include <linux/input.h>
#include <math.h>
#include <dirent.h>
#include <ctype.h>
#include <errno.h>
#include <linux/hwmsensor.h>
#include <linux/sensors_io.h>
#include "Keypad.h"
#include "libhwm.h"

#define GSENSOR_NAME  "/dev/gsensor"
#define OPEN_FILE_FAIR -1 
#define OPEN_SUCCESS 0
#define RETURE_SUCCESS 0
#define ENABLED_SUCCESS 0
#define GSENSOR_CALIBRATION_SUCCESS 0
#define ENABLED_FAIR -2 
#define CALIBRATION_FAIR -3 
#define SET_CALIBRATION_FAIR -4
#define WRITE_NVRAM_FAIR  -5 

int gs_fd ; 

//打开gsensor
int gs_open(char *gs_name) ;
//关闭gsensor
int gs_close(int fd) ;
//gsensor开始工作
int gs_enable(unsigned int command) ;
//校准gsensor
int gs_calibration(unsigned int cali_delay ,unsigned int cali_num ,unsigned int cali_tolerance) ;

int main(void)
{
	int gs_ret = 0 ;
	int  cali_delay = 50;
int  cali_num = 20;
//这里的20表示把校准的数值做20次平均
//如果想要更精确,也可以做40次平均计算
    int  cali_tolerance = 20 ; //40
	//打开gsensor
	gs_ret = gs_open(GSENSOR_NAME);
	if(gs_ret != 0){
		printf("gs open fair!\n") ;
		return -1 ;
	}
	//使能gsensor
	gs_ret = gs_enable(GSENSOR_IOCTL_INIT);
	if(gs_ret != 0){
		printf("gs enable fair!\n") ;
		return -2 ;
	}
	//校准---->包括:执行校准的动作、设置校准数值、将校准数值写入nvram
	gs_ret = gs_calibration(cali_delay,cali_num,cali_tolerance);
	if(gs_ret != 0){
		printf("gs_calibration fair!\n") ;
		return -3 ;
	}
	//关闭gsensor
	gs_ret = gs_close(gs_fd);
	if(gs_ret != 0){
		printf("gs_close fair!\n");
		return -4 ;
	}
	printf("runexec call gsensorCalibrate end\n");
    return 0 ;
}

//1、open
int gs_open(char *gs_name)
{
	gs_fd = open(gs_name,O_RDONLY) ;
	if(gs_fd < 0)
	{
		printf("open gsensor dev fair!\n") ;
		return OPEN_FILE_FAIR ;
	}
	printf("gsensor open success!\n");
	return OPEN_SUCCESS ;
}

//2、enable gsensor
int gs_enable(unsigned int command)
{
	int err = 0; 
	unsigned int flags = 1;
	int  max_retry = 3, retry_period = 100, retry=0;
	while ((err = ioctl(gs_fd, command, &flags)) && (retry ++ < max_retry)) ;
	usleep(retry_period*1000);
    if (err) {
        printf("enable g-sensor fail: %s", strerror(errno));
        return ENABLED_FAIR;
	}
	printf("enable gsensor success!\n");
	return ENABLED_SUCCESS ;
}
//3、校准
int gs_calibration(unsigned int cali_delay ,unsigned int cali_num ,unsigned cali_tolerance)
{
	int  err ;
	int flag = 0;
	HwmData dat; //dat.x  dat.y  dat.z 
	HwmData cali;
	HwmData cali_nvram;
	while(1)
	{
		//执行校准的动作
		err = gsensor_calibration(gs_fd , cali_delay , cali_num , cali_tolerance , 0 , &cali);
		if(err != 0)
		{
			printf("calibrate acc: %d\n", err); 
			return CALIBRATION_FAIR ;
		}
		//设置校准cali,让校准数据生效
		err = gsensor_set_cali(gs_fd,&cali) ;
		if(err != 0)
		{
			printf("set calibration fail: (%s) %d\n", strerror(errno), err);
			return SET_CALIBRATION_FAIR ;
		}
		//将校准数据写入nvram中
		err = gsensor_write_nvram(&cali) ;
		if(err != 0)
		{
			printf ("write nvram fail: (%s) %d\n", strerror(errno), err);
			return WRITE_NVRAM_FAIR ;
		}
		flag = 1 ;
		if(flag == 1)
		{
			printf("Gsensor calibrate success!\n") ;
			break ;
		}
	}
	return GSENSOR_CALIBRATION_SUCCESS ;
}

//关闭
int gs_close(int fd)
{
	close(fd) ;
	return 0 ;
}

然后写一个简单的Android.mk

LOCAL_PATH:=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SHARED_LIBRARIES += libcutils libutils   libhwm
LOCAL_STATIC_LIBRARIES += libz libstdc++ 
LOCAL_SRC_FILES:= \
	gs_cali.c 
	
LOCAL_MODULE:= gsensor_calibrate
include $(BUILD_EXECUTABLE)

将.c和Android.mk一并放在external下的一个自己定义的文件夹中,比如gs_cali文件夹,然后最后编译会在out目录下生成gsensor_calibrate这个二进制文件。

特别需要注意一点:使用这种方法进行校准后,需要将gsensor驱动的自动校准功能屏蔽,具体屏蔽方法还需要分析驱动源代码。

但这仅仅只是校准数值而已,如何把校准完的数值读出来呢?参考ftm_gsensor.c这个文件源代码,最终得知是通过打开/dev/gsensor这个节点,然后通过总线访问机制获取:

/sys/bus/platform/drivers/gsensor/sensordata这个文件存放的数据。

读取Gsensor的x,y,z的数据的方法可以参考static int gsensor_read(struct acc_priv *acc)这个函数的实现方法,但它的方法和我的是有区别的,MTK中实现的gsensor_read方法是读取从驱动中读取已经转化成十进制的数据,而我读取的是原始数据十六进制,所以必须要做进制转换,我们可以来看下这个函数:

static int gsensor_read(struct acc_priv *acc)
{
	static char buf[128];    
	int x, y, z, err;

	if(acc->fd == -1)
	{
		FTGLOGE("invalid file descriptor\n");
		err = -EINVAL;
	}
	else if((acc->support_selftest == 1) && (!acc->selftest) && (err = gsensor_selftest(acc, 10, 20)))
	{    
		FTGLOGE("selftest fail: %s(%d)\n", strerror(errno), errno);
	}
	else
	{
		//使用GSENSOR_IOCTL_READ_SENSORDATA命令获取sensor的数据,并存储在buf里
		err = ioctl(acc->fd, GSENSOR_IOCTL_READ_SENSORDATA, buf);
		if(err)
		{
			FTGLOGE("read data fail: %s(%d)\n", strerror(errno), errno);
		}
		//从buf中将x,y,z三个值取出来
		else if(3 != sscanf(buf, "%x %x %x", &x, &y, &z))
		{
			FTGLOGE("read format fail: %s(%d)\n", strerror(errno), errno);
		}
		else
		{
			//除以1000,并转成浮点数
			acc->evt.x = (float)(x)/1000;
			acc->evt.y = (float)(y)/1000;
			acc->evt.z = (float)(z)/1000;
			err = 0;
			gsensor_statistic(acc);
			//返回gsensor数据
			//add sensor data to struct sp_ata_data for PC side
			return_data.gsensor.g_sensor_x = acc->evt.x;
			return_data.gsensor.g_sensor_y = acc->evt.y;
			return_data.gsensor.g_sensor_z = acc->evt.z;
			return_data.gsensor.accuracy = 3;
			
		}
	}
	return err;    
}

预知详情,可以去分析factory工厂测试的源码,看看具体实现,这里就简单的一笔带过了。

当然我们也可以采用:./pskyed/libs/em_emmc_comm/libhwm/include/libhwm.h这里面的读取gsensor数据的接口,如果要用就需要在Android.mk中包含相关的动态库。

使用adb进入Android根文件系统,此时,通过cat命令可以得知/sys/bus/platform/drivers/gsensor/sensordata这个文件存放的数据并不是字符串,而是十六进制数据,以下是机器平放在桌子上读取的x,y,z的数值:

Sensordata文件的数值:    算法转换:

-------------------------------------------------------------------------------------------------------------------------------

X:0000                                    转十进制(float)0/1000    =  0

Y:0072                                    转十进制(float)114/ 1000  = 0.114

Z:264f                                    转十进制(float)9807/ 1000 = 9.807

-------------------------------------------------------------------------------------------------------------------------------

x,y,z数值的校准范围是(0,0,9.8),以上数值说明机器校准后的数值是正确的,误差比较小。

在Window上用Notepad++打开这个文件看到里面的数据如下。

使用Winhex查看sensordata这个文件

以下是我实现的方法:

//Gsensor读取

//这个文件存放的数据不是字符串,而是十六进制数据,所以必须做进制转换,然后才能转成浮点数

#define	GSENSOR_NAME "/sys/bus/platform/drivers/gsensor/sensordata"
//存储gsensor节点数据的结构体
struct gsensor_info
{
	//x,y,z坐标值
	char x[10] ;
	char y[10] ;
	char z[10] ;
};
int Hex_to_dec(const char* str)  ;
static int check_flag ;
int Gsensor_Test()
{
	struct gsensor_info g ;
	char ln[80];//用于存取读出数据的数组
	FILE *f;
	int i,n;
	char *p;
	int x,y,z ;
	float x1,y1,z1 ;
	int xv = 0 , yv = 0 , zv = 0 ;
	while(1)
	{
		f=fopen(GSENSOR_NAME,"r");//打开txt文件
		if(NULL==f){
			printf("Can not open file sensordata!\n");//判断是否可以打开文件
			return 1;
		}
		i=0;
		while (1)
		{
		   //从文件中读取一行数据
		   if (NULL==fgets(ln,80,f)) 
				break;
		   p = ln ; 
		   sscanf(p,"%s%s%s",g.x,g.y,g.z);
		   i++;
		}
		//存储的数据是十六进制,需要做数据转换
		x = Hex_to_dec(g.x);
		y = Hex_to_dec(g.y);
		z = Hex_to_dec(g.z);
		x1 = (float)x / 1000 ;
		y1 = (float)y / 1000 ;
		z1 = (float)z / 1000 ;
		printf("x: %4.2f   y:%4.2f  z:%4.2f\n",x1,y1,z1);
		break ;
	}
	return 0 ;
	while_r:
	while(1);
}
//16进制转10进制算法实现
int Hex_to_dec(const char* str)  
{  
    int value;  
  
    if (! str)  
    {  
        return 0;  
    }  
    value = 0;  
    while (1)  
    {  
        if ((*str >= '0') && (*str <= '9'))  
        {  
            value = value*16 + (*str - '0');  
        }  
        else if ((*str >= 'A') && (*str <= 'F'))  
        {  
            value = value*16 + (*str - 'A') + 10;  
        }  
        else if ((*str >= 'a') && (*str <= 'f'))  
        {  
            value = value*16 + (*str - 'a') + 10;  
        }  
        else  
        {  
            break;  
        }  
        str++;  
    }  
    return value;  
} 








目录
相关文章
|
11月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
1020 4
|
3月前
|
安全 数据库 Android开发
在Android开发中实现两个Intent跳转及数据交换的方法
总结上述内容,在Android开发中,Intent不仅是活动跳转的桥梁,也是两个活动之间进行数据交换的媒介。运用Intent传递数据时需注意数据类型、传输大小限制以及安全性问题的处理,以确保应用的健壯性和安全性。
186 11
|
3月前
|
监控 Android开发 数据安全/隐私保护
批量发送短信的平台,安卓群发短信工具插件脚本,批量群发短信软件【autojs版】
这个Auto.js脚本实现了完整的批量短信发送功能,包含联系人管理、短信内容编辑、发送状态监控等功能
|
5月前
|
存储 XML Java
Android 文件数据储存之内部储存 + 外部储存
简介:本文详细介绍了Android内部存储与外部存储的使用方法及核心原理。内部存储位于手机内存中,默认私有,适合存储SharedPreferences、SQLite数据库等重要数据,应用卸载后数据会被清除。外部存储包括公共文件和私有文件,支持SD卡或内部不可移除存储,需申请权限访问。文章通过代码示例展示了如何保存、读取、追加、删除文件以及将图片保存到系统相册的操作,帮助开发者理解存储机制并实现相关功能。
1176 2
|
7月前
|
存储 编解码 监控
Android平台GB28181执法记录仪技术方案与实现
本文介绍了大牛直播SDK的SmartGBD在执法记录仪场景中的应用。GB28181协议作为视频监控联网的国家标准,为设备互联互通提供规范。SmartGBD专为Android平台设计,支持音视频采集、编码与传输,具备自适应算法和多功能扩展优势。文章分析了执法记录仪的需求,如实时音视频传输、设备管理及数据安全,并详细阐述了基于SmartGBD的技术实现方案,包括环境准备、SDK集成、设备注册、音视频处理及功能扩展等步骤。最后展望了SmartGBD在未来智慧物联领域的广阔应用前景。
275 13
|
7月前
|
存储 编解码 开发工具
Android平台毫秒级低延迟HTTP-FLV直播播放器技术探究与实现
本文详细探讨了在Android平台上实现HTTP-FLV播放器的过程。首先介绍了FLV格式的基础,包括文件头和标签结构。接着分析了HTTP-FLV传输原理,通过分块传输实现流畅播放。然后重点讲解了播放器的实现步骤,涵盖网络请求、数据解析、音视频解码与渲染,以及播放控制功能的设计。文章还讨论了性能优化和网络异常处理的方法,并总结了HTTP-FLV播放器的技术价值,尤其是在特定场景下的应用意义。
264 11
|
8月前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
472 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
7月前
|
监控 Shell Linux
Android调试终极指南:ADB安装+多设备连接+ANR日志抓取全流程解析,覆盖环境变量配置/多设备调试/ANR日志分析全流程,附Win/Mac/Linux三平台解决方案
ADB(Android Debug Bridge)是安卓开发中的重要工具,用于连接电脑与安卓设备,实现文件传输、应用管理、日志抓取等功能。本文介绍了 ADB 的基本概念、安装配置及常用命令。包括:1) 基本命令如 `adb version` 和 `adb devices`;2) 权限操作如 `adb root` 和 `adb shell`;3) APK 操作如安装、卸载应用;4) 文件传输如 `adb push` 和 `adb pull`;5) 日志记录如 `adb logcat`;6) 系统信息获取如屏幕截图和录屏。通过这些功能,用户可高效调试和管理安卓设备。
|
10月前
|
IDE 开发工具 Android开发
移动应用开发之旅:探索Android和iOS平台
在这篇文章中,我们将深入探讨移动应用开发的两个主要平台——Android和iOS。我们将了解它们的操作系统、开发环境和工具,并通过代码示例展示如何在这两个平台上创建一个简单的“Hello World”应用。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧,帮助你更好地理解和掌握移动应用开发。
243 17
|
12月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
382 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库

热门文章

最新文章