精华内容
下载资源
问答
  • 【安卓开发系列 -- 系统开发】安卓开发流程梳理 -- 从底层到 APP (Linux 驱动开发) 【0】开发环境简介 1. 主机,Win10;虚拟机,Ubuntu 16.04 2. 开发板,AIO-3399C 六核 AI 开发板 3. android 源码,android 7.1...

    【安卓开发系列 -- 系统开发】安卓开发流程梳理 -- 从底层到 APP (Linux 驱动开发)

    【0】开发环境简介

    1. 主机,Win10;虚拟机,Ubuntu 16.04
    2. 开发板,AIO-3399C 六核 AI 开发板
    3. android 源码,android 7.1

    【1】Linux 驱动开发

    Linux 驱动开发 Makefile 文件

    ifneq ($(KERNELRELEASE),)
     
    obj-m += freg.o
    
    else
    	# 指定内核代码树
    	KERNELDIR := /home/shallysun/proj/firefly-rk3399-Industry/kernel
    	# 获取代码所在的当前目录
    	PWD := $(shell pwd)
    	# 指定交叉编译链
    	ARCH = arm64
    	CROSS_COMPILE=/home/shallysun/proj/firefly-rk3399-Industry/prebuilts/gcc/linux-x86/aarch64/
    aarch64-linux-android-4.9/bin/aarch64-linux-android-
    	
    .PHONY: modules clean
    
    modules:
    	# 编译内核驱动模块
    	$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) modules
    clean:
    	# 清除中间文件
    	@rm -rf *.o *.order *.symvers *.mod.* .*.o.cmd .*.mod.o.cmd .*.ko.cmd .tmp_versions *.ko
    	
    endif

    Linux 驱动开发 驱动示例源码 (Android 系统源代码情景分析(第三版) 第二章 源码)

    驱动示例代码 H 头文件

    #ifndef _FAKE_REG_H_
    #define _FAKE_REG_H_
    
    #include <linux/cdev.h>
    #include <linux/semaphore.h>
    
    #define FREG_DEVICE_NODE_NAME  "freg"
    #define FREG_DEVICE_FILE_NAME  "freg"
    #define FREG_DEVICE_CLASS_NAME "freg"
    
    struct fake_reg_dev {
    	int val;
    	struct semaphore sem;
    	struct cdev dev;
    };
    
    #endif

    驱动示例代码 C 文件

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/types.h>
    #include <linux/fs.h>
    #include <linux/proc_fs.h>
    #include <linux/device.h>
    #include <asm/uaccess.h>
    #include <linux/slab.h>
    
    #include "freg.h"
    
    // 指定主设备号与从设备号
    static int freg_major = 0;
    static int freg_minor = 0;
    
    // 设备类别和设备变量
    static struct class* freg_class = NULL;
    static struct fake_reg_dev* freg_dev = NULL;
    
    /******************************************************************************************************
     * 设备文件的操作方法
     ******************************************************************************************************/
    // 打开设备文件函数
    static int freg_open(struct inode* inode, struct file* filp);
    // 释放设备文件函数
    static int freg_release(struct inode* inode, struct file* filp);
    // 读设备文件函数
    static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);
    // 写设备文件函数
    static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos);
    // 传统设备文件操作方法表,定义了一个 file_operations 结构体变量,并制定了其中的打开、释放、读、写操作函数
    static struct file_operations freg_fops = {
            .owner = THIS_MODULE,
            .open = freg_open,
            .release = freg_release,
            .read = freg_read,
            .write = freg_write,
    };
    
    /******************************************************************************************************
     * devfs 文件系统的设备属性操作方法
     ******************************************************************************************************/
    // 显示寄存器的值
    static ssize_t freg_val_show(struct device* dev, struct device_attribute* attr,  char* buf);
    // 写入寄存器
    static ssize_t freg_val_store(struct device* dev, struct device_attribute* attr, const char* buf, 
    size_t count);
    // devfs 文件系统的设备属性
    static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, freg_val_show, freg_val_store);
    
    /******************************************************************************************************
     * 设备文件的操作方法
     ******************************************************************************************************/
    static int freg_open(struct inode* inode, struct file* filp) {
    	struct fake_reg_dev* dev;
    	/**
    	 * container_of(ptr, type, member) 函数的实现包括两部分 :
    	 * 1.  判断 ptr 与 member 是否为同意类型
         * 2.  计算 size 大小,结构体的起始地址 = (type *)((char *)ptr - size)   (注:强转为该结构体指针)
    	 * 
    	 * 作用 : 通过一个结构变量中一个成员的地址找到这个结构体变量的首地址
    	 */
    	dev = container_of(inode->i_cdev, struct fake_reg_dev, dev);
    	filp->private_data = dev;
    
    	return 0;
    }
    
    static int freg_release(struct inode* inode, struct file* filp) {
    	return 0;
    }
    
    static ssize_t freg_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) {
    	ssize_t err = 0;
    	struct fake_reg_dev* dev = filp->private_data;
    
    	/**
    	 * int down_interruptible(struct semaphore *sem)
    	 * 这个函数的功能就是获得信号量,如果得不到信号量就睡眠,此时没有信号打断,那么进入睡眠;
    	 * 但是在睡眠过程中可能被信号打断,打断之后返回-EINTR,主要用来进程间的互斥同步;
    	 */
    	if(down_interruptible(&(dev->sem))) {	
    		return -ERESTARTSYS;
    	}
    
    	if(count < sizeof(dev->val)) {
    		goto out;
    	}
    	/**
    	 * 原型 	: unsigned long copy_to_user(void *to, const void *from, unsigned long n)
    	 * to		: 目标地址(用户空间)
    	 * from		: 源地址(内核空间)
    	 * n		: 将要拷贝数据的字节数
    	 * 返回		: 成功返回0,失败返回没有拷贝成功的数据字节数
    	 */
    	if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) {
    		err = -EFAULT;
    		goto out;
    	}
    
    	err = sizeof(dev->val);
    
    out:
    	// 唤醒进程
    	up(&(dev->sem));
    	return err;
    }
    
    static ssize_t freg_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) {
    	struct fake_reg_dev* dev = filp->private_data;
    	ssize_t err = 0;
    
    	if(down_interruptible(&(dev->sem))) {
    			return -ERESTARTSYS;
    	}
    
    	if(count != sizeof(dev->val)) {
    			goto out;
    	}
    	/**
    	 * 原型 	: unsigned long copy_from_user(void *to, const void *from, unsigned long n);
    	 * to 		: 目标地址(内核空间)
    	 * from 	: 源地址(用户空间)
    	 * n 		: 将要拷贝数据的字节数
    	 * 返回 	:成功返回0,失败返回没有拷贝成功的数据字节数
    	 */
    	if(copy_from_user(&(dev->val), buf, count)) {
    		err = -EFAULT;
    		goto out;
    	}
    
    	err = sizeof(dev->val);
    
    out:
    	// 唤醒进程
    	up(&(dev->sem));
    	return err;
    }
    
    /******************************************************************************************************
     * devfs 文件系统的设备属性操作方法
     ******************************************************************************************************/
    static ssize_t __freg_get_val(struct fake_reg_dev* dev, char* buf) {
    	int val = 0;
    
    	if(down_interruptible(&(dev->sem))) {
    			return -ERESTARTSYS;
    	}
    
    	val = dev->val;
    	up(&(dev->sem));
    
    	return snprintf(buf, PAGE_SIZE, "%d\n", val);
    }
    
    static ssize_t __freg_set_val(struct fake_reg_dev* dev, const char* buf, size_t count) {
    	int val = 0;
    	// 将一个字符串转换成 unsigend long long 型数据
    	val = simple_strtol(buf, NULL, 10);
    
    	if(down_interruptible(&(dev->sem))) {
    			return -ERESTARTSYS;
    	}
    
    	dev->val = val;
    	up(&(dev->sem));
    
    	return count;
    }
    
    static ssize_t freg_val_show(struct device* dev, struct device_attribute* attr, char* buf) {
    	struct fake_reg_dev* hdev = (struct fake_reg_dev*)dev_get_drvdata(dev);
    	
            return __freg_get_val(hdev, buf);
    }
    
    static ssize_t freg_val_store(struct device* dev, struct device_attribute* attr, const char* buf, 
    size_t count) {
    	 struct fake_reg_dev* hdev = (struct fake_reg_dev*)dev_get_drvdata(dev);
    
            return __freg_set_val(hdev, buf, count);
    }
    
    // 初始化设备
    static int  __freg_setup_dev(struct fake_reg_dev* dev) {
    	int err;
    	/**
    	 * MKDEV,将主设备号与次设备号转换为 dev_t 类型的数据
    	 */
    	dev_t devno = MKDEV(freg_major, freg_minor);
    
    	memset(dev, 0, sizeof(struct fake_reg_dev));
    
    	// 静态的方式初始化字符设备
    	cdev_init(&(dev->dev), &freg_fops);
    	dev->dev.owner = THIS_MODULE;
    	dev->dev.ops = &freg_fops;
    	// 调用 cdev_add() 函数将初始化之后的 cdev 添加到系统中去;
    	err = cdev_add(&(dev->dev),devno, 1);
    	if(err) {
    		return err;
    	}	
    	// 初始化信号量
    	sema_init(&(dev->sem), 1);
    	dev->val = 0;
    
    	return 0;
    }
    
    // 驱动初始化函数
    static int __init freg_init(void) { 
    	int err = -1;
    	dev_t dev = 0;
    	struct device* temp = NULL;
    
    	printk(KERN_ALERT"Initializing freg device.\n");
    	// alloc_chrdev_region 是让内核分配一个尚未使用的主设备号
    	err = alloc_chrdev_region(&dev, 0, 1, FREG_DEVICE_NODE_NAME);
    	if(err < 0) {
    		printk(KERN_ALERT"Failed to alloc char dev region.\n");
    		goto fail;
    	}
    	// 获取主设备号与从设备号
    	freg_major = MAJOR(dev);
    	freg_minor = MINOR(dev);
    	/**
    	 * 原型 : void *kmalloc(size_t size, int flags);
    	 * size 要分配内存的大小,以字节为单位.
    	 * flags 要分配内存的类型
    	 * #define GFP_KERNEL(__GFP_WAIT | __GFP_IO | __GFP_FS)
    	 * __GFP_WAIT 	: 缺内存页的时候可以睡眠;
     	 * __GFP_IO 	: 允许启动磁盘IO;
    	 * __GFP_FS 	: 允许启动文件系统IO;
    	 */
    	freg_dev = kmalloc(sizeof(struct fake_reg_dev), GFP_KERNEL);
    	if(!freg_dev) {
    		err = -ENOMEM;
    		printk(KERN_ALERT"Failed to alloc freg device.\n");
    		goto unregister;
    	}
    	// 初始化设备
    	err = __freg_setup_dev(freg_dev);
    	if(err) {
    		printk(KERN_ALERT"Failed to setup freg device: %d.\n", err);
    		goto cleanup;
    	}
    	// 在 /sys/class/ 目录下创建设备类别目录
    	freg_class = class_create(THIS_MODULE, FREG_DEVICE_CLASS_NAME);
    	if(IS_ERR(freg_class)) {
    		err = PTR_ERR(freg_class);
    		printk(KERN_ALERT"Failed to create freg device class.\n");
    		goto destroy_cdev;
    	}
    	// 在 /dev/ 目录和 /sys/class/freg 目录下分别创建设备文件 freg
    	temp = device_create(freg_class, NULL, dev, "%s", FREG_DEVICE_FILE_NAME);
    	if(IS_ERR(temp)) {
    		err = PTR_ERR(temp);
    		printk(KERN_ALERT"Failed to create freg device.\n");
    		goto destroy_class;
    	}
    
    	/**
    	 * dev_attr_val 结合宏 DEVICE_ATTR 理解
    	 */
    	err = device_create_file(temp, &dev_attr_val);
    	if(err < 0) {
    		printk(KERN_ALERT"Failed to create attribute val of freg device.\n");
            goto destroy_device;
    	}
    
    	dev_set_drvdata(temp, freg_dev);
    
    	printk(KERN_ALERT"Succedded to initialize freg device.\n");
    
    	return 0;
    
    // 错误处理
    destroy_device:
    	device_destroy(freg_class, dev);
    destroy_class:
    	class_destroy(freg_class);
    destroy_cdev:
    	cdev_del(&(freg_dev->dev));	
    cleanup:
    	kfree(freg_dev);
    unregister:
    	unregister_chrdev_region(MKDEV(freg_major, freg_minor), 1);	
    fail:
    	return err;
    }
    
    // 驱动退出函数
    static void __exit freg_exit(void) {
    	dev_t devno = MKDEV(freg_major, freg_minor);
    
    	printk(KERN_ALERT"Destroy freg device.\n");
    	// 销毁设备类别和设备
    	if(freg_class) {
    		device_destroy(freg_class, MKDEV(freg_major, freg_minor));
    		class_destroy(freg_class);
    	}
    	// 删除字符设备和释放设备内存
    	if(freg_dev) {
    		cdev_del(&(freg_dev->dev));
    		kfree(freg_dev);
    	}
    	// 释放设备号资源
    	unregister_chrdev_region(devno, 1);
    }
    // 驱动模块信息
    MODULE_LICENSE("GPL");
    MODULE_DESCRIPTION("Fake Register Driver");
    // 注册驱动的初始化与卸载函数
    module_init(freg_init);
    module_exit(freg_exit);

    安卓安装 Linux 驱动模块

    adb push <linux 内核驱动模块> /data (安卓根文件系统中的路径)
    
    insmod <linux 内核驱动模块名称>.ko      安装驱动模块
    rmmod <linux 内核驱动模块名称>          卸载驱动模块
    lsmod                                查看安装的模块
    dmesg                                查看模块装、卸日志信息

    测试 Linux 驱动模块

    方法一,测试 /sys/class

    方法二,编写 C 代码测试 /dev/freg

    测试代码示例

    #include <stdio.h>
    #include <stdlib.h>
    #include <fcntl.h>
    
    #define FREG_DEVICE_NAME "/dev/freg"
    
    int main(int argc, char** argv)
    {
    	int fd = -1;
    	int val = 0;
    
    	fd = open(FREG_DEVICE_NAME, O_RDWR);
    	if(fd == -1)
    	{
    		printf("Failed to open device %s.\n", FREG_DEVICE_NAME);
    		return -1;
    	}
    	
    	printf("Read original value:\n");
    	read(fd, &val, sizeof(val));
    	printf("%d.\n\n", val);
    
    	val = 5;
    	printf("Write value %d to %s.\n\n", val, FREG_DEVICE_NAME);
            write(fd, &val, sizeof(val));
    
    	
    	printf("Read the value again:\n");
            read(fd, &val, sizeof(val));
            printf("%d.\n\n", val);
    
    	close(fd);
    
    	return 0;
    }
    LOCAL_PATH := $(call my-dir)
    # 每个 Android.mk 文件必须以定义 LOCAL_PATH 为开始,它用于在开发 tree 中查找源文件;
    # 宏 my-dir 则由 Build System 提供,返回包含 Android.mk 的目录路径;
    include $(CLEAR_VARS)
    # CLEAR_VARS 变量由 Build System 提供,并指向一个指定的 GNU Makefile;
    # 由它负责清理很多 LOCAL_xxx,但不清理LOCAL_PATH
    LOCAL_MODULE_TAGS := optional
    # user 		: 指该模块只在user版本下才编译 
    # eng 		: 指该模块只在eng版本下才编译 
    # tests 	: 指该模块只在tests版本下才编译
    # optional 	: 指该模块在所有版本下都编译
    LOCAL_MODULE := freg
    # LOCAL_MODULE 模块必须定义,以表示 Android.mk 中的每一个模块,名字必须唯一且不包含空格;
    # Build System 会自动添加适当的前缀和后缀;
    LOCAL_SRC_FILES := $(call all-subdir-c-files)
    # LOCAL_SRC_FILES 变量必须包含将要打包如模块的 C/C++ 源码
    # 不必列出头文件,build System 会自动帮我们找出依赖文件
    include $(BUILD_EXECUTABLE)
    # BUILD_STATIC_LIBRARY 		: 编译为静态库
    # BUILD_SHARED_LIBRARY 		: 编译为动态库
    # BUILD_EXECUTABLE 		: 编译为 Native C 可执行程序

    基于 AIO-3399C 六核 AI 开发板编译驱动测试文件

    source build/envsetup.sh
    标准版:lunch rk3399_firefly_aioc-userdebug
    AI  版:lunch rk3399_firefly_aioc_ai-userdebug
    mmm ./external/freg/
    编译成功后,结果将保存在 ./out/target/product/rk3399_firefly_aioc_ai/system/bin 之中
    将编译好的可执行文件上传到 android 系统,
    chmod 777 freg,改变文件性质
    ./freg 执行文件进行测试
    
    注意 : 将驱动测试 C 源文件放置在 external 目录下执行编译
    

    测试结果

    常见问题总结

    问题 1. implicit declaration of function ‘kmalloc’
    
    解决方案 : 
    添加 #include <linux/slab.h>
    
    
    问题 2. init_MUTEX 的调用编译出错
    
    解决方案 :
    init_MUTEX 已经被禁用,改为 sema_init(&(dev->sem), 1);
    
    问题 3. Android adb shell data 目录,Permission denied
    
    解决方案 :
    安卓系统的控制台中 su root 获取 root 权限
    修改 /data 目录权限,chmod 777 /data
    Windows 控制台中,adb push <Linux 驱动模块> /data;上传 Linux 驱动模块到安卓系统中;
    
    问题 4. No command 'mmm' found
    
    解决方案 :
    使用m、mm、mmm命令之前要在android源码目录下执行命令
    . build/envsetup.sh

     

    参考致谢

    本博客为博主学习笔记,同时参考了网上众博主的博文以及相关专业书籍,在此表示感谢,本文若存在不足之处,请批评指正。

    【1】Android 系统源代码情景分析(第三版) [ M ] 罗升阳

    【2】实现内核驱动程序模块

    【3】驱动模块编译错误提示--implicit declaration of function ‘kmalloc’

    【4】内核模块 (三) 安卓环境下编译ko文件

    【5】在Ubuntu上为Android系统编写Linux内核驱动程序 + 编写加载动态模块ko

    【6】No command 'mmm' found

    【7】Android.mk 文件语法详解

    【8】LOCAL_MODULE_TAGS 选项说明

    【9】container of()函数简介

    【10】深入浅出down_interruptible

    【11】linux驱动开发--copy_to_user 、copy_from_user函数实现内核空间数据与用户空间数据的相互访问

    【12】信号量机制中的down和up函数

    【13】device_create_file的使用

    展开全文
  • 不适用weexpack和weex-toolkit开发安卓项目

    打开android studio,新建一个android工程,目录结构如下


    app是我们的安卓项目空间,weex文件夹是我们的weex开发空间


    配置好webpack,我们可以利用android studio的terminal控制台进行we文件编译

    将src里面的we文件编译到安卓的assets文件加下,方便加载js文件

    webpack run build

    然后我们就直接在AS里面写我们的we和java了,不用频繁更换编辑器和IDE。
    展开全文
  • 然而也必须说明一点,不论是Android开发还是iOS开发,虽然都出现了相关的程序员供大于求的情况,但市场仍然是有需求的,特别是对资深的开发人员及拥有相关底层开发知识的应用程序员市场及发展还是很多的;...

    开头

    很多人说Android开发前景越来越差了 我觉得这个回答是片面的

    首先Android应用开发前景差是在最近两年出现的,也就是从2018开始,从那时起移动端的程序员已经慢慢出现供大于求的局面,本人作为移动端开发,深知这一点。

    然而也必须说明一点,不论是Android开发还是iOS开发,虽然都出现了相关的程序员供大于求的情况,但市场仍然是有需求的,特别是对资深的开发人员及拥有相关底层开发知识的应用程序员市场及发展还是很多的;这里所讲的就业难都是相对于初级开发人员。

    为什么会在18年出现应用端就业难?这是由于在前几年App风盛行,那几年只要是个和互联网的公司要是没个自己的App那都不好意思叫互联网公司,所以一般的互联网公司成立之初就会着手开发自己的App,不管是否是刚需,但市场终究是严峻的;App虽然好,能快速开展本公司的业务,但App的运营成本还是很高的,一个App在早期就开发团队来说一般都是需要至少Android开发一人,iOS开发一人,后台开发2人以上,还有UI及产品等等,当然最主要的问题是一般的公司对于这种模式都会面临回报周期长的问题,App开发完了还面临着推广的问题,市场抢占不了,也许之前的都会付之东流。于是在早期很多学校特别是培训机构就针对市场推出了应用端的培训课程,短到三个月长到半年的课程,于是乎每一个月都会有成千上万的应用端走向市场,所以时间一长,市场慢慢趋于稳定。应用端的人员慢慢就变得供大于求了,这也是慢慢出现应用端求职难的问题了。

    以上说的是其中一方面,成本高和其他低成本,低运营的技术相继出现也是应用端求职难现象的助推手。

    这里主要讲下2016年出现的小程序对于App的冲击,原生App有着开发周期长,运营成本高及回报周期长的特点,所以近年来很多公司都慢慢削去了应用端从而将资金放在其他部分,直到小程序的出现,开发成本一下就可以降下不少,为什么呢,因为小程序的开发语言可以由前端的开发人员承担,而且开发一个小程序不需要维护两套代码也就是不需要像原生App一样,需要Andoid端和iOS端,因为小程序是运行在微信中的,所以只要开发人员维护一套代码就够了,这大大的降低了前期的开发成本,其他细节在这就不细讲了,总之小程序的出现不同程度上更加冲击了应用端的竞争市场,从而也使得应用端就业竞争更加激烈。

    当然市场还是需要应用端的人才的,这里所说的人才指的是那些会顺应技术发展和时代的有着不断学习力的开发人员,对于那些初级的程序员市场终究对于他们是残酷的,所以提升自己的竞争力在任何职业任何时候都是非常重要的,只有这样才不会被市场所抛弃。

    就先写到这,手机码字很痛苦,写的很片面不好之处敬请指出,如果觉得有参考价值的朋友也可以关注一下我 我会定期分享一些关于Android进阶方面的知识,也会分享一下最新的面试题~

    历时半年,我们终于整理出了这份市面上最全面的最新Android面试题解析大全!

    章节目录

    第一章:Android 基础 面试题
    第二章:Android 高级 面试题
    第三章:开源框架实战面试解析
    第四章:Java 面试题
    第五章:Flutter相关面试题全解析
    第六章:一线大厂Android高频面试题集锦

    这份最新整理的面试解析包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目加真题技术点和思维解析
    可以说,如果你熟知这份PDF里面的大部分知识点(熟知,而不是深入理解原理和架构),随便去哪个互联网公司面试个20k以上的移动开发岗位很简单。

    以下截图为这本PDF的目录索引,大家可以快速翻阅,是否有感兴趣或者薄弱点,查漏补缺或者深入学习都很不错,;
    目录1
    目录2
    目录3
    目录4
    目录5
    目录6

    总结

    写到这里也结束了,在文章最后放上一个小小的福利,以下为小编自己在学习过程中整理出的一个关于Flutter的学习思路及方向,从事互联网开发,最主要的是要学好技术,而学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯,更加需要准确的学习方向达到有效的学习效果。
    由于内容较多就只放上一个大概的大纲,需要更及详细的学习思维导图的 点击我的GitHub免费获取。
    还有免费的高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。

    lutter全方面的Android进阶实践技术资料,并且还有技术大牛一起讨论交流解决问题。**

    跨平台开发:Flutter.png

    展开全文
  • 对于 Android 开发而言,继续从事 UI 与用户交互逻辑也是一条谋生之道,但是受市场红利结束、开发成本等影响,拓展能力成为大前端开发也是趋势之一。 在我的 《我的移动开发春季历程》 说过:随着各类移动跨平台的...

    前言

    在博主认为,对于Android面试以及进阶的最佳学习方法莫过于刷题+博客+书籍+总结,前三者博主将淋漓尽致地挥毫于这篇博客文章中,至于总结在于个人,实际上越到后面你会发现面试并不难,其次就是在刷题的过程中有没有去思考,刷题只是次之,这又是一个层次了,这里暂时不提后面再谈。

    博主总结了一系列大厂面试中常问的面试技术点,深入解析以及答案,将为最近准备面试的各开发者去大厂保驾护航,

    何谓面试? 博主所理解的面试,它是一个过程,是不断沉淀、不断总结、善于传达自己的专业领域技术以及解决问题能力的过程。以下是博主总结的一些面试题,文中如有错误,恳请批评指正!

    一 、Java 后端

    在我写过的 《Android程序员的Java后台学习建议》 中提到,因为 Java 的先天优势,Android 开发者在转岗选择时,转 Java 后端占据很大比例,因为 Java 后端岗位需求量较大,同时技术迁移成本也较低,比如现在 Java 开发中应用最广泛的 Spring全家桶:

    img

    作为 Android 开发者,如果你使用过或了解过 Dagger,那么理解和使用 Spring并不难。通过各类注解如 @Contorller@Service@Configuration等去标明类的用途,通过@Autowired 去注入使用的方式,相信会让你倍感亲切。

    如果说 Spring 是类似 Dagger 的注入和管理实体的容器框架,那么 SpringMVC 就是基于 Spring功能之上添加的 Web 框架。

    对于 Android 开发而言,MVC 肯定是不会陌生的,实际上后台开发人员通常就是写个 Controller 提供个 Model给你。而 HibernateMyBatis 都是持久层框架,属于能方便使用如 MySql 等数据库的 ORM 框架。

    如果说 SpringSpringMVC 是 Android 开发者较友好的选择,那么 Spring Boot 则是你的“绿色通道”。 Spring Boot又称之为全家桶,因为它集齐了各类常用的开发框架,同时降低了 Spring 开发的门槛,更是简化了各种配置过程,而且 Jetbrains大家庭支持它!所以现阶段而言,个人认为 SpringSpringMVCSpringBoot 是 Android 开发者学习入门 Java Web 的不错选择。

    img

    当然,作为 Android 开发者转后端,最大的问题就是开发思路和领域的变化,你需要关注的不再是当个设备上的业务逻辑,更需要关注的是大数据、并发、性能优化、分布式等等,这些都是转岗的你需要从头开始的,这是无可避免的成本。

    二、大前端

    大前端在我理解中,一般包括 Web 前端、客户端、跨平台的集合。对于 Android 开发而言,继续从事 UI 与用户交互逻辑也是一条谋生之道,但是受市场红利结束、开发成本等影响,拓展能力成为大前端开发也是趋势之一。

    在我的 《我的移动开发春季历程》 说过:随着各类移动跨平台的兴起,在 ReactNative、Weex、Flutter、 等框架的加持下,Android开发的能力已不再局限于 Android 领域,而各大厂商近些年也针对跨平台的不足进行逐步完善。

    但这并不意味着原生开发的“没落”!无论哪种跨平台的加持,最终都离不开你的原生系统能力。大潮过去后更多是淘汰“裸泳”,而如今的市场愈发趋向大前端,竞争之下我觉得移动开发的边界可以更广。

    所以有 Android 平台存在就会有需求,而利用跨平台开发,能让我们的能力更快的覆盖到 iOS 平台,同时如 ReactNativeFlutterWeex 等,能够让你逐步接触并熟悉 Web 前端的世界,之后如 uni-appmyvueChameleontaro 等框架还能丰富你的小程序能力,从而让你的业务能力走向新的巅峰。

    img

    三、物联网

    Android 平台和 iOS 平台最不同的就是它的开源和影响力,多年后的今天,Android 已经不仅仅代表着手机平台,在电视、机顶盒、楼宇安防、自动售卖、广告展牌、智能家电、车载等领域都已渗透并精进,就像我 Android QQ 群内的小伙伴,现在还从事 App 开发的反而不多,但是在以上领域继续耕耘 Android 的反而很多。

    5G 时代下物联网领域很有可能具备新的机会,华为的鸿蒙智慧屏、无人驾驶的车载平板、其他各类利用 Android 实现的无线智能平台,这都是 Android 开发者可以耕耘的机会。

    当然,光是学习或者“纸上谈兵”,最终只会徒生焦虑,所以‘学习最好的方法就是“动手”,开源项目是你最好的学习方法,自己动手练练,才是你“转岗”的最好尝试。

    总结

    学习技术是一条慢长而艰苦的道路,不能靠一时激情,也不是熬几天几夜就能学好的,必须养成平时努力学习的习惯。所以:贵在坚持!

    最后如何才能让我们在面试中对答如流呢?

    答案当然是平时在工作或者学习中多提升自身实力的啦,那如何才能正确的学习,有方向的学习呢?有没有免费资料可以借鉴?为此我整理了一份Android学习资料路线:

    这里是一部分我工作以来以及参与过的大大小小的面试收集总结出来的一套BAT大厂面试资料专题包,在这里免费分享给大家,主要还是希望大家在如今大环境不好的情况下面试能够顺利一点,希望可以帮助到大家。需要的小伙伴们可以点击我的GitHub获取免费领取方式

    好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划,可以去我的主页加一下技术群。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

    最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!

    他们的面试以及工作经验完善自己的之后的面试计划及职业规划。

    最后,祝愿即将跳槽和已经开始求职的大家都能找到一份好的工作!

    这些只是整理出来的部分面试题,后续会持续更新,希望通过这些高级面试题能够降低面试Android岗位的门槛,让更多的Android工程师理解Android系统,掌握Android系统。喜欢的话麻烦点击一个喜欢再关注一下~

    展开全文
  • 学习福利 【Android 详细知识点思维脑图(技能树)】 其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到...
  • 21世纪,智能手机走进了人们的生活,现在的智能手机的操作系统基本分为两种,一种是...安卓APP的开发流程是什么?让我们一起来看一下。安卓APP开发流程?1、需求沟通在app开发前,需要对app进行详细的分析。首先对a...
  • 安卓项目开发流程

    千次阅读 2018-01-15 16:22:48
    一.环境部署 1.安装jdk  2.安装android sdk 【manager】  3.运行 android studio 设置android sdk 位置。重要! 4.新建一个APP 等待 bandle构建项目。
  • 目录安卓APP开发流程前言一、流程概览二、分步概述1.需求分析2.产品原型设计3.UI视觉设计4、数据库搭建5、服务端开发6、Android客户端开发7、APP程序测试8、上传到应用商店9、APP的维护以及更新总结 安卓APP开发流程...
  • eclipse安卓开发环境配置全流程前言eclipse安卓开发环境的配置:1.jdk及eclipse的安装配置2.Android SDK的安装配置3.eclipse ADT 的安装配置4.安卓依赖库的手动添加 前言 流行的安卓开发软件有eclipse和Android ...
  • 安卓App开发流程

    2016-03-24 22:58:07
    一. 应用规划 决定App要实现的功能 决定基本的业务逻辑 决定是否需要服务器端支持 决定是否需要本地数据库支持 决定是否需要可能需要特殊权限 二. 架构设计 分层, 网络连接 安全或加密 ...三....整合及测试
  • 【安卓开发系列 -- 系统开发】编译 android 系统 (基于 AIO-3399C) ...【安卓开发系列 -- 系统开发】安卓开发流程梳理 -- 从底层到 APP (Linux 驱动开发) 【安卓开发系列 -- 系统开发】字符设备驱动基础 ...
  • uniapp安卓原生插件开发流程

    千次阅读 2020-12-16 14:10:05
    文章目录记录下uni原生插件开发流程一、准备开发环境二、开发过程2.读入数据总结 记录下uni原生插件开发流程 看官方文档看的有点头蒙 特此记录下 官方文档链接 一、准备开发环境 JAVA环境 jdk1.8 Android Studio ...
  • 安卓开发的工作流程

    千次阅读 2018-10-26 18:47:57
    我们的安卓开发基本上都在 Linux 系统下进行,发行版是 Ubuntu,版本建议 14.04(12.04 和 16.04 在某些安卓版本上都会出编译错误,参考  faq: 安装使用 ubuntu-16.04 注意事项 )。 装好 Linux 系统...
  • 安卓BLE开发教程(二) BLE开发流程

    千次阅读 2018-12-20 18:33:58
    安卓上进行BLE开发时,就不必像理解BLE协议栈那样复杂了。因为安卓的BLE包为我们提供了十分丰富的API、各类常量、各类连接通信情况下的回调API等。 具体流程 一、声明权限 二、获取Adapter适配器 三、开启蓝牙...
  • 为什么需要用到DNK(也就是jni)?因为安卓有很多功能是实现不了的...OK,言归正传,看一下NDK的简单开发流程(确保自己可以把NDK操作完整地跑一边,就基本上可以明白DNK的作用了) 第一步:下载ndk以及配置ndk路径...
  • 现在用下面的命令找到keytool.exe的位置 flutter doctor -v 这时候你直接拷贝命令并进行输入,但这里也有个坑,就是如果文件夹中间带有空空,你需要用带引号扩上。 D:\Program\Android\'Android Studio'\jre\bin...
  • 开发安卓安装流程 0 安装操作系统 Win10 用户名称尽量英文字母加数字,避免编码问题 1 安装Java sdk 1.8.0_45 所需文件 jdk-8u45-windows-x64 1.1 正常安装 1.1 配置Java环境 1)右击【我的电脑】---【属性...
  • 基于TCP的安卓客户端开发详细流程
  • 背景在安卓开发学习之ListView的测量流程源码阅读一文中,我记录了ListView的onMeasure()过程,今天,我继续记录下ListView的onLayout()流程AbsListView#onLayoutListView并没有实现onLayout(),所以它调用的是父类...
  • 最近做的工作,主要是对安卓的一系列UI组件的开发,在完成设计需求的过程中,有了一点小想法,今天来把他们总结一下。 一、了解安卓中的View体系 在UI开发的过程中,我们一定会使用到多种view组件,为了帮助我们更好...
  • 那么今天创胜小编就跟大家详细探讨一下快速开发一款定制APP都具有哪些流程内容?梳理需求在开发前期,企业或者创业者需要明确自己APP想要实现的功能,最好是自己梳理一份详细的需求文档,这样有利于跟开发公司更好的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,158
精华内容 463
关键字:

安卓开发流程