Google Android Developer

简介: Google Android Developer 我记得我学习Android那会儿,老师也不是很会Android,所有经常是对着Google的文档读,再加上全英文的时候文档,看起来还是很吃力,经常是开着有道词典一句一句的翻译,等后来工作了,才意识到,...

Google Android Developer

我记得我学习Android那会儿,老师也不是很会Android,所有经常是对着Google的文档读,再加上全英文的时候文档,看起来还是很吃力,经常是开着有道词典一句一句的翻译,等后来工作了,才意识到,这玩意是个好东西呀,但是那个时候已经不怎么去看API指南了,后来经常看一些技术书籍,我发现很多的写书人也大多数是看着官方文档的印子来写的,于是又重新打开了官网,这么一番下来,啧啧有味,你不光能看到各个版本的新技术,和需要注意的地方,还能看到很多我们不常用,却很实在的小get技术,再后来,也就是Google的中国版官网发布

我们来看看他有什么有趣的东西吧!

一.index

这里写图片描述

他的首页,我们可以看到这么一个目录

  • 首页
    • Android
    • Wear
    • TV
    • Auto
    • Things
  • 设计
    • Material Design
  • 开发
    • 培训
    • API指南
    • 参考
    • 示例
    • Android Studio
    • Android NDK
    • Google Services
    • 质量检测
    • Kotlin
  • 分发
    • Google Play
    • Play 控制台
    • 最佳做法
    • 营销工具
    • 政策中心
  • 故事
  • 预览

我们作为Android开发者,其实大部分都是必须要是了解的,但是我们可以侧重的去看一些东西

  • 首页
    • Android
  • 设计
    • Material Design
  • 开发
    • 培训
    • API指南
    • 参考
    • 示例
    • Android Studio
    • Android NDK
    • 质量检测
    • Kotlin

这些就是我们比较侧重的东西了,如果你是一名Android,我也希望你把这些全部去看完去熟悉,这样对你的职业生涯还是很有帮助的,我们本篇的主要内容也是介绍这些东西

二.Guide

这里写图片描述

作为第二大块,我准备说一下我们的API指南,因为这真是太赞了,我的很多的开发技巧都是上门学习到的,先来看下目录:

  • 简介

    • 应用基础知识
    • 设备兼容性
    • 系统权限
  • 平台架构

    • JAVA8性能
    • 在ART上验证用户行为
  • 应用组件

    • Intent和Intent过滤器
      • 通用Intent
    • Activity
      • Fragment
      • 加载器
      • 任务和返回栈
      • 预览屏幕
    • 服务
      • 绑定服务
      • AIDL
    • 内容提供者
      • 内容提供程序技术知识
      • 创建内容提供程序
      • 日历提供程序
      • 联系人提供程序
      • 存储访问程序
    • 应用小组件
      • 应用小部件主机
    • 进程和线程
  • 应用资源

    • 概览
    • 提供资源
    • 访问资源
    • 处理运行时变更
    • 本地化
      • ICU4J Android框架API
      • 语音和语音区域
    • 资源类型
      • 动画
      • 颜色状态列表
      • 可绘制对象
      • 布局
      • 菜单
      • 字符串
      • 样式
      • 其他类型
  • 应用清单

    • action
    • activity
    • activity-alias
    • application
    • category
    • compatible-screens
    • data
    • grant-uri-permission
    • instrumentation
    • intent-filter
    • manifest
    • meta-data
    • path-permission
    • permission
    • permission-group
    • permission-tree
    • provider
    • receiver
    • service
    • supports-gi-texture
    • supports-screens
    • uses-configuration
    • uses-feature
    • uses-library
    • uses-permission
    • uses-permission-sdk-23
    • uses-sdk
  • 用户界面

    • 概览
    • 布局
      • 线性布局
      • 相对布局
      • 列表视图
      • 网格视图
    • 输入控件
      • 按钮
      • 文本字段
      • 复选框
      • 单选按钮
      • 切换按钮
      • 微调框
      • 选取器
    • 输出事件
    • 菜单
    • 设置
    • 对话框
    • 通知
    • Toast
    • 搜索
      • 创建搜索界面
      • 添加近期查询建议
      • 添加自定义建议
      • 可搜索配置
    • 多窗口支持
    • 拖放
    • 无障碍功能
      • 为应用设置无障碍功能
      • 无障碍功能开发者检查单
      • 否件无障碍服务
    • 样式和主题
    • 自定义组件
  • 动画和图形

    • 概览
    • 属性动画
    • 视图动画
    • 可绘制动画
    • OpenGL ES
    • 硬件加速
  • 计算

    • 渲染脚本
    • 高级渲染脚本
    • RunTime API参考
      • 数学类型
      • 对象类型
      • 转换函数
      • 数学常量和函数
      • 矢量数学函数
      • 矩阵函数
      • 四元数函数
      • 原子更新函数
      • 时间函数和类型
      • 分配数据访问函数
      • 对象特征函数
      • 内核调用函数和类型
      • 输入/输出函数
      • 调试函数
      • 图形函数和类型
      • 索引
  • 媒体和相机

    • 媒体回放
    • 媒体路由器
    • 媒体路由提供程序
    • ExoPlayer
    • 支持的媒体格式
    • 音频采集
    • JetPlayer
    • 相机
  • 位置和传感器

    • 位置和传感器
    • 位置策略
    • 传感器概览
    • 运动传感器
    • 位置传感器
    • 环境传感器
  • 连接

    • 蓝牙
      • BLE
    • NFC
      • NFC基础知识
      • 高级NFC
      • 基于主机的卡模拟
    • WI-FI P2P
    • USB
      • 配件
      • 主机
    • SIP
  • 文本和输入

    • 复制和粘贴
    • 创建IME
    • 拼写检查程序
  • 数据存储

    • 存储选项
    • 数据备份
    • 应用安装位置
    • 支持库
      • 功能
      • 设置
      • 修订历史吉利
    • 数据绑定库
    • 测试支持库
      • API参考
  • 管理

    • 设备政策
  • 网络应用

    • 在网络应用中支持不同屏幕
    • 在WebView中构建网络应用
    • 迁移到Android4.4中的WebView
    • 调试网络应用
    • 网络应用最佳做法
  • 最佳实践

    • 支持多种屏幕
      • 分配到特定屏幕
      • 屏幕兼容性模式
    • 支持平板电脑和手机

我们可以发现的事就是,这个目录承载着我们很多的只是点,没错,很多知识,不光是我们经常用到的,而是我们不经常用到,但是他却十分的爽歪歪的东西,我们这篇文章就是来探讨这些知识点的,不要怀疑,这件事情一定很酷。好的,那,既然如此,那我们继续来简化他吧,我们把小get点都提取出来

2.1 系统权限

应用在安装的时候,会为每个软件包提供唯一的 Linux 用户 ID,我们都只是Linux有用户组,同样的Android也有,如果我们需要管理员权限的话,在Android中的实现是在AndroidManifest.xml 的 manifest 标记中使用 sharedUserId 属性,举例:

<manifest
    package="com.liuguilin.googlesample"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:sharedUserId="android.uid.system">

2.2 Java 8

如果想使用JAVA8的一些特性,比如Lambda表达式,注解之类的,可以在Android Studio中配置Gradle

android {
  ...
  defaultConfig {
    ...
    jackOptions {
      enabled true
    }
  }
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

要注意的是,目前JAVA8还不支持Instant Run

2.3 Intent

Intent十分的强大,是沟通各个组件之间的桥梁,我们可以用它做很多的事情

1.闹钟

如果我们用Intent来创建闹钟,可以这样

    //创建闹钟
    public void createAlarm(String message, int hour, int minutes) {
        Intent intent = new Intent(AlarmClock.ACTION_SET_ALARM)
                .putExtra(AlarmClock.EXTRA_MESSAGE, message)
                .putExtra(AlarmClock.EXTRA_HOUR, hour)
                .putExtra(AlarmClock.EXTRA_MINUTES, minutes);
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }
    }

当然,这只是跳转到设置系统闹钟,而且我们必须加上权限

<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>

以及过滤器

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.SET_ALARM" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

这样我们就可以实现点击设置系统闹钟了,而且这些属性,有很多,我们就不细讲了,有兴趣的就可以看下吧。

Intent地址

这里写图片描述

2.定时器

同样的,定时器也是系统的,我们可以这样做

 //创建定时器
    public void startTimer(String message, int seconds) {
        Intent intent = new Intent(AlarmClock.ACTION_SET_TIMER)
                .putExtra(AlarmClock.EXTRA_MESSAGE, message)
                .putExtra(AlarmClock.EXTRA_LENGTH, seconds)
                .putExtra(AlarmClock.EXTRA_SKIP_UI, false);
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }
    }

同样的特也需要权限以及过滤器,这里注意一下过滤器只是接受到这个intent的标识

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.SET_TIMER" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

这里写图片描述

3.日历

如果添加日历的事件提醒呢,说实话,这个没多少人用,因为你需要先登录Google账号,OK,我们看下代码

    //创建日历事件
    public void addEvent(String title, String location, Calendar begin, Calendar end) {
        Intent intent = new Intent(Intent.ACTION_INSERT)
                .setData(CalendarContract.Events.CONTENT_URI)
                .putExtra(CalendarContract.Events.TITLE, title)
                .putExtra(CalendarContract.Events.EVENT_LOCATION, location)
                .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, begin)
                .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, end);
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivity(intent);
        }
    }

如果你也想接收这个事件的话,你也需要添加intent过滤器

<activity ...>
    <intent-filter>
        <action android:name="android.intent.action.INSERT" />
        <data android:mimeType="vnd.android.cursor.dir/event" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

这里写图片描述

4.相机

如需打开相机应用并接收拍摄的照片或视频,请使用 ACTION_IMAGE_CAPTURE 或 ACTION_VIDEO_CAPTURE 操作。此外,还可在 EXTRA_OUTPUT extra 中指定您希望相机将照片或视频保存到的 URI 位置,所有用起来还是很方便的,我相信,相机很多人都会,但是视频,其实没有多少人会,ok,我们来看下实现,这里我就直接贴代码了

    //拍照返回
    public void capturePhoto(String targetFilename) {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT,
                Uri.withAppendedPath(mLocationForPhotos, targetFilename));
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, PHOTO_CODE);
        }
    }

    //视频返回
    private void dispatchTakeVideoIntent() {
        Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
        if (takeVideoIntent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(takeVideoIntent, VIDEO_CODE);
        }
    }

这样你就可以跳转到相机了,如果想要拿到他的返回值,还得通过intent,也就是在回调里面:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.i(TAG, "requestCode" + requestCode + "resultCode" + resultCode);
        if (resultCode == RESULT_OK) {
            switch (requestCode) {
                case PHOTO_CODE:
                    if (data != null) {
                        Bundle bundle = data.getExtras();
                        Bitmap thumbnail = (Bitmap) bundle.get("data");
                        if (thumbnail != null) {
                            ivSrc.setImageBitmap(thumbnail);
                        }
                    }
                    break;
                case VIDEO_CODE:
                    if (data != null) {
                        Uri videoUri = data.getData();
                        mVideoView.setVideoURI(videoUri);
                    }
                    break;
            }
        }
    }

5.联系人

联系人的话还稍微复杂一点,不过这都是通用的基础语法,最多只能说是麻烦了一点,OK,我们来看下怎么跳转到联系人,然后选择联系人

    //选择联系人
    public void selectContact() {
        Intent intent = new Intent(Intent.ACTION_PICK);
        intent.setType(ContactsContract.CommonDataKinds.Phone.CONTENT_TYPE);
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, REQUEST_SELECT_PHONE_NUMBER);
        }
    }

这里还好,跳转到联系人,然后主要是接收到的回调,我们需要自己去查找我们需要的表单

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK) {
            switch (requestCode) {
                case REQUEST_SELECT_PHONE_NUMBER:
                    Uri contactUri = data.getData();
                    String[] projection = new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER};
                    Cursor cursor = getContentResolver().query(contactUri, projection,
                            null, null, null);
                    if (cursor != null && cursor.moveToFirst()) {
                        int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
                        String number = cursor.getString(numberIndex);
                        Toast.makeText(this, "number:" + number, Toast.LENGTH_SHORT).show();
                    }
                    break;
            }
        }
    }

这里其实还有很多的隐藏用法,比如插入,查询联系人等等,还有发送文件,以及我们打开指定的文件类型,这些都是我们很有必要掌握的,做文件管理器的时候就要用到,只能说某个领域专注某个模块吧,但是大的方向还是要了解下

这里写图片描述

2.4 Fragment

Fragment我们这里就只说一下他的两个扩展类,DialogFragment和PreferenceFragment

1.DialogFragment

DialogFragment我一直不是很清楚他的优势在哪,但是的确看到有一些好的开源项目里面有用到,这里就说一下他的简单用法

首先是show出来,并且给上dialog的tag

    public void showDialog() {
        mStackLevel++;

        android.support.v4.app.FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        android.support.v4.app.Fragment prev = getSupportFragmentManager().findFragmentByTag("dialog");
        if (prev != null) {
            ft.remove(prev);
        }
        ft.addToBackStack(null);

        DialogFragment newFragment = MyDialogFragment.newInstance(mStackLevel);
        newFragment.show(ft, "dialog");
    }

这个没什么问题,主要来看下Fragment的实现,这里代码是官网的,有一点繁琐,其实多看下家就明白了,只是一个更换主题

public class MyDialogFragment extends DialogFragment {

    int mNum;

    public static MyDialogFragment newInstance(int num) {
        MyDialogFragment f = new MyDialogFragment();
        Bundle args = new Bundle();
        args.putInt("num", num);
        f.setArguments(args);
        return f;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mNum = getArguments().getInt("num");

        int style = DialogFragment.STYLE_NORMAL, theme = 0;
        switch ((mNum - 1) % 6) {
            case 1:
                style = DialogFragment.STYLE_NO_TITLE;
                break;
            case 2:
                style = DialogFragment.STYLE_NO_FRAME;
                break;
            case 3:
                style = DialogFragment.STYLE_NO_INPUT;
                break;
            case 4:
                style = DialogFragment.STYLE_NORMAL;
                break;
            case 5:
                style = DialogFragment.STYLE_NORMAL;
                break;
            case 6:
                style = DialogFragment.STYLE_NO_TITLE;
                break;
            case 7:
                style = DialogFragment.STYLE_NO_FRAME;
                break;
            case 8:
                style = DialogFragment.STYLE_NORMAL;
                break;
        }
        switch ((mNum - 1) % 6) {
            case 4:
                theme = android.R.style.Theme_Holo;
                break;
            case 5:
                theme = android.R.style.Theme_Holo_Light_Dialog;
                break;
            case 6:
                theme = android.R.style.Theme_Holo_Light;
                break;
            case 7:
                theme = android.R.style.Theme_Holo_Light_Panel;
                break;
            case 8:
                theme = android.R.style.Theme_Holo_Light;
                break;
        }
        setStyle(style, theme);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_dialog, container, false);
        View tv = v.findViewById(R.id.text);
        ((TextView) tv).setText("Dialog #" + mNum );

        Button button = (Button) v.findViewById(R.id.show);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                ((DialogFragmentActivity) getActivity()).showDialog();
            }
        });
        return v;
    }

2.PreferenceFragment

PreferenceFragment是我觉得非常实用而且很棒的扩展类了,他能完美的实现设置的一些需求,我们来看下官网对他的描述

以列表形式显示 Preference 对象的层次结构,类似于 PreferenceActivity。这在为您的应用创建“设置” Activity 时很有用处

Ok,首先我们把一个fragment添加进去Activity

   <fragment
        android:name="com.liuguilin.googlesample.fragment.PrefsFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:tag="pre"/>

好的,其实这个fragment特也没干什么,你需要继承的是PreferenceFragment,然后调用添加资源

addPreferencesFromResource(R.xml.preferences);

我们再res下创建xml

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android">

    <PreferenceCategory
        android:title="设置">

        <CheckBoxPreference
            android:key="checkbox_preference"
            android:summary="这是一个不正经的勾选"
            android:title="勾选"/>

    </PreferenceCategory>

    <PreferenceCategory
        android:title="用户">

        <EditTextPreference
            android:dialogTitle="请输入"
            android:key="edittext_preference"
            android:summary="我想知道你的性别"
            android:title="性别"/>

        <ListPreference
            android:dialogTitle="用戶属性"
            android:entries="@array/entries_list_preference"
            android:entryValues="@array/entryvalues_list_preference"
            android:key="list_preference"
            android:summary="身份证请保密"
            android:title="请输入身份证"/>

    </PreferenceCategory>

    <PreferenceCategory
        android:title="启动设置">

        <PreferenceScreen
            android:key="screen_preference"
            android:summary="启动预览请调整手机方向"
            android:title="旋转">

            <CheckBoxPreference
                android:key="next_screen_checkbox_preference"
                android:summary="锁定方向后不可旋转"
                android:title="锁定屏幕"/>

        </PreferenceScreen>

        <PreferenceScreen
            android:summary="官网是你的家"
            android:title="官网">

            <intent
                android:action="android.intent.action.VIEW"
                android:data="http://www.android.com"/>

        </PreferenceScreen>
    </PreferenceCategory>
    <PreferenceCategory
        android:title="工程模式">

        <CheckBoxPreference
            android:key="parent_checkbox_preference"
            android:summary="打开后你将拥有管理员权限"
            android:title="是否打开"/>

        <CheckBoxPreference
            android:dependency="parent_checkbox_preference"
            android:key="child_checkbox_preference"
            android:layout="?android:attr/preferenceLayoutChild"
            android:summary="管理员权限"
            android:title="ROOT"/>

    </PreferenceCategory>

</PreferenceScreen>

好的,运行起来是不是很酷,如果想详细的了解他的每个节点的使用,可以去看下api文档

API文档

这里写图片描述

2.5 AIDL

再来看下AIDL,一般的应用并不会使用到aidl,但是aidl确实IPC通信的一个很好的辅助手段

AIDL的话,要注意的几点就是首先要相互调用需要在同一包名,还要注意就是接口的填写,我们来看文档

官网上有一段说明:

注:只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL。 如果您不需要执行跨越不同应用的并发 IPC,就应该通过实现一个 Binder 创建接口;或者,如果您想执行 IPC,但根本不需要处理多线程,则使用 Messenger 类来实现接口。无论如何,在实现 AIDL 之前,请您务必理解绑定服务。

很好的诠释了AIDL,如果你不需要跨应用的话,建议使用其他的跨进程来操作

那我们如何定义呢,首先要在main目录下创建一个aidl的文件夹,然后就是三步流程

  • 1.创建 .aidl 文件

此文件定义带有方法签名的编程接口。

  • 2.实现接口

Android SDK 工具基于您的 .aidl 文件,使用 Java 编程语言生成一个接口。此接口具有一个名为 Stub 的内部抽象类,用于扩展 Binder 类并实现 AIDL 接口中的方法。您必须扩展 Stub 类并实现方法。

  • 3.向客户端公开该接口

实现 Service 并重写 onBind() 以返回 Stub 类的实现。

当你知道这些步骤之后,我们就可以开始来编写了

package com.android.liuguilin;

interface IRemoteService{

    int add(int a,int b);
}

这是我们定义的一个接口。我在里面只定义了一个add方法,传两个int,然后我们来看下接口服务这边的定义

public class RemoteService extends Service {

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    private IRemoteService.Stub mBinder = new IRemoteService.Stub() {

        @Override
        public int add(int a, int b) throws RemoteException {

            return a + b;
        }
    };
}

ok,其实这里就是实现了一个相加的逻辑,然后把binder对象返回回去,我们最后直接bindservice就好了

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIRemoteService = IRemoteService.Stub.asInterface(service);
            isBinder = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mIRemoteService = null;
            isBinder = false;
        }
    };

ok,拿到对象之后就可以直接去使用了,这里注意一下,如果需要跨进程,需要在清单文件中

        <service
            android:name=".service.RemoteService"
            android:exported="true"/>

2.6 多窗口

android:resizeableActivity=["true" | "false"]

2.7 硬件加速

<application android:hardwareAccelerated="true" ...>

2.8 BLE

低功耗蓝牙在最近的几年应该算是很流行的了,那我们怎么去开发他呢,其实Google的文档已经详细到不能再详细了,我们一起来看下吧!

不管你是普通的蓝牙还是BLE,你都需要把权限给我加上

    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

如果你想你的程序只有支持BLE的能用,那可以这样

    <uses-feature
        android:name="android.hardware.bluetooth_le"
        android:required="true"/>

而且你在一开始,你就要判断他是否支持低功耗蓝牙

        //判断是否支持BLE
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, "不支持BLE设备", Toast.LENGTH_SHORT).show();
            //finish();
        }

然后我们就可以做一些初始化的操作了

        //获取本地适配器
        BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            mBluetoothAdapter = bluetoothManager.getAdapter();
        }

        //如果没有打开,请求打开
        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }

这里还得注意下,BLE是API18之后出现的,然后开始搜索

   //搜索
    private void scanLeDevice(final boolean enable) {
        if (enable) {
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, 1000);
            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    }

    //搜索回调
    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if(device != null){
                        Log.i(TAG,"devices:"  + device.getName());
                    }
                }
            });
        }
    };

这样就搜索到了,如果需要通信,需要实现GATT客户端,具体的使用方法,大家可以看文档

BLE API DOC

2.9 NFC

2.10 WIFI-P2P

2.11 USB

2.12 复制粘贴

2.13 Data Binding

2.14 WebView

到这里大家有什么详细的就自己去细看吧!

三.Material Design

不过国内还是有中文版的

就是有点小尴尬

MD的设计风格一致很棒,很平滑的过渡动画,空间设计感,阴影等,不过如此,还有色彩,图片,图像等,我们把这些称为调色板,不过具体的代码实现需要看具体的面板,这份只是介绍他的一些思想,我们也不过多的去探索,毕竟在国内,还是很少有做原生风格的app的公司

四.Android Studio

AS目前来看,还真是很好用,但是如果在配置不是很强的电脑上,体验会查很多,而且还要配置好多项配置才行,还是有一定的麻烦,我个人对他的唯一不足的就是没有更多官方支持的Theme,这会让编程失去了很多的乐趣,好的,我们来看下我们这门课所学的知识

3.1 向您的项目添加 C 和 C++ 代码

官方建议我们使用2.2或者更高的版本,如果你不是很了解JNI,你可以先看一会儿JNI的文档

Android Studio 用于构建原生库的默认工具是 CMake,在此之前,你要先把build.properties里的这句代码删掉

android.useDeprecatedNdk = true

而使用NDK编译的话,你需要下载几个东西

  • NDK
  • CMake
  • LLDB

这里写图片描述

其次是创建一个支持C/C++的项目,其实在我们新建工程的时候有一个选项的,就是注入C/C++的代码

这里写图片描述

大致的步骤官网也说的很详细

  • 1.在向导的 Configure your new project 部分,选中 Include C++ Support 复选框。
  • 2.点击 Next。
  • 3.正常填写所有其他字段并完成向导接下来的几个部分。
  • 4.在向导的 Customize C++ Support 部分,您可以使用下列选项自定义项目:
    • C++ Standard:使用下拉列表选择您希望使用哪种 C++ 标准。选择 Toolchain Default 会使用默认的 CMake 设置。
    • Exceptions Support:如果您希望启用对 C++ 异常处理的支持,请选中此复选框。如果启用此复选框,Android Studio 会将 -fexceptions 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。
    • Runtime Type Information Support:如果您希望支持 RTTI,请选中此复选框。如果启用此复选框,Android Studio 会将 -frtti 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。
  • 5.点击 Finish。

这里写图片描述

当然,你手动在src下创建cpp也是可以的,只不过需要设置好配置文件,我们来看下他为我们创建了什么东西,首先就是我们的c文件了

#include <jni.h>
#include <string>

extern "C"
JNIEXPORT jstring JNICALL
Java_com_liuguilin_ndksample_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

可以看到这里很简单,就定义了一个stringFromJNI方法,返回的是Hello from C++字符串,所以我们要用的话,就直接去加载他然后调用就好了

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    public native String stringFromJNI();
}

这个时候运行后可以看到他自动为我们编译好了so,也不需要我们自己去用NDK编译了,很方便的,这就是CMake所带来的好处

我们来看下官方给我们的流程说法

  • 1.Gradle 调用您的外部构建脚本 CMakeLists.txt。
  • 2.CMake 按照构建脚本中的命令将 C++ 源文件 native-lib.cpp 编译到共享的对象库中,并命名为 libnative-lib.so,Gradle 随后会将其打包到 APK 中。
  • 3.运行时,应用的 MainActivity 会使用 System.loadLibrary() 加载原生库。现在,应用可以使用库的原生函数 stringFromJNI()。
  • 4.MainActivity.onCreate() 调用 stringFromJNI(),这将返回“Hello from C++”并使用这些文字更新 TextView。

这里写图片描述

3.2 使用 Lint 改进您的代码

在终端使用‘gradlew lint’来对项目进行检索就好了,详细的请看他的api

3.3 配置方法数超过 64K 的应用

65536其实是件很烦恼的事情,这个时候虽然官方建议我们尽量优化代码,但是还是为时已晚,官方给出的解决方案

如果您的 minSdkVersion 设置为 21 或更高值,您只需在模块级 build.gradle 文件中将 multiDexEnabled 设置为 true 就好了

android {
    defaultConfig {
        ...
        minSdkVersion 21 
        targetSdkVersion 25
        multiDexEnabled true
    }
    ...
}

但是,如果您的 minSdkVersion 设置为 20 或更低值,你就需要处理了,你不光要开启multiDexEnabled,还需要添加multidex的依赖

android {
    defaultConfig {
        ...
        minSdkVersion 15 
        targetSdkVersion 25
        multiDexEnabled true
    }
    ...
}

dependencies {
  compile 'com.android.support:multidex:1.0.1'
}

并且,如果你没有自己实现Application,那你只需要在Application里添加

    <application
            android:name="android.support.multidex.MultiDexApplication" >
        ...
    </application>

不过你都超过了65536,想必你的项目应该挺大的吧,那你应该有自己的Application,那你就需要这样去写了,先去继承MultiDexApplication

  @Override
  protected void attachBaseContext(Context base) {
     super.attachBaseContext(context);
     Multidex.install(this);
  }

这样就可以分包了

五.Android NDK

六.Kotlin

Kotlin最近虽然火了一把,官方也支持了Kotlin,但是目前来看,还是坑要多一点,而且AS要支持需要升级到3.0以及JAVA8,可以参考API

具体的用法和使用C/C++有点类似,创建项目的时候勾选Kotlin的支持

这里写图片描述

更多的语法支持,可以去Kotlin官网看看,目前个人觉得,还是把源码和View,事件处理等知识点学习好才是真理

好了,到这里就over了,可能写的不是很详细,但是也是初步的把官方文档梳理了一遍,等我下次详细的再翻阅一遍再次记述一遍,应该就很详细了,这是黄金屋,大家一定要研读!

对文章有兴趣的可以关注一下我的微信公众号或者QQ群

Android旅行的路途:Android旅行的路途

这里写图片描述

目录
相关文章
|
3月前
|
IDE API 开发工具
Google I/O :Android Jetpack 最新变化(四)Compose
Google I/O :Android Jetpack 最新变化(四)Compose
102 0
|
3月前
|
JSON IDE 测试技术
Google I/O :Android Jetpack 最新变化(二) Performance
Google I/O :Android Jetpack 最新变化(二) Performance
112 0
|
3月前
|
SQL API Android开发
Google I/O :Android Jetpack 最新变化(一) Architecture
Google I/O :Android Jetpack 最新变化(一) Architecture
68 0
|
6月前
|
API 开发工具 Android开发
解决 Android App 上架 Google play后 ,签名变更,第三方sdk无法登录
解决 Android App 上架 Google play后 ,签名变更,第三方sdk无法登录
146 0
|
7月前
|
传感器 安全 Android开发
Google发布 Android 12 开发预览版
Google发布 Android 12 开发预览版
84 1
|
API Android开发 图形学
【Unity3D】Android App Bundle(aab)打包上架Google Play介绍
总体说来,Android App Bundle打包有3种方式,每种方式都有成功上架Google Play进行测试通过,因此实用程度还是挺高的。能够理解以下内容的前提是会打apk包,知道如何生成Asset Bundle文件,这块内容可以参考我的上一篇文章。
1074 0
【Unity3D】Android App Bundle(aab)打包上架Google Play介绍
|
3月前
|
API Android开发
Google I/O :Android Jetpack 最新变化(三)UI
Google I/O :Android Jetpack 最新变化(三)UI
49 0
|
6月前
|
JavaScript Java 开发工具
Cocos Creator Android 平台接入 Google Firebase (Analytics功能)(二)
Cocos Creator Android 平台接入 Google Firebase (Analytics功能)
155 0
|
6月前
|
存储 JavaScript Java
Cocos Creator Android 平台 Google 原生登录(二)
Cocos Creator Android 平台 Google 原生登录
124 0
|
6月前
|
API 开发工具 Android开发
Cocos Creator Android 平台 Google 原生登录(一)
Cocos Creator Android 平台 Google 原生登录
185 0