2019年8月

Android自发布以来就已经存在权限,但是之前版本(Android 6.0以前)对用户安全和隐私数据保护是很有限的,特别是常用离不开的App,很容易造成用户数据泄露。为了解决这个问题,Android6.0开始引入运行时权限

Android权限机制

Android中如果要访问网络、监听开机广播、监听来电或者短信息、监听网络变化等等,就必须在AndroidManifest.xml文件中添加权限声明,比如:

<!-- 网络状态 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 系统开机 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

运行时权限:用户在安装软件的时候可以不用一次性授权所有权限,而是在软件的实际使用过程中再逐一对某一权限进行申请授权。
Android的权限可以归为2类:普通权限、危险权限。

普通权限:不会直接威胁到用户安全和隐私的权限,对于这部分权限,系统会自动帮我们进行授权,而不用用户进行手动全操作授权;

危险权限:是指会获取用户敏感数据或对设备安全性造成影响的权限,如联系人、定位位置、拨打电话等,对于这部分权限,必须由用户进行手动点击进行授权,否则程序无法使用该权限对应的功能。

危险权限一共是9组24个权限,9组分别为日历、拍照、联系人、位置、麦克风、电话、传感器、短信及存储:

- 阅读剩余部分 -

Android中针对数据的持久化存储主要提供了3种:文件存储SharedPreference存储SQLite数据库存储。除了上面3种外,也可也将数据保存在设备的SD卡中,不过使用这3种相对更简单易用。

文件存储

Android中对文件存储的内容不进行任何格式化处理,所有的数据都是原样保存至文件中,因此比较适合存储一些文本内容或二进制数据。
存储的文件默认都是存储在路径:/data/data/{package_name}/files这个目录下,{package_name}为程序的包名。

将数据保存至文件

Context类中提供openFileOutput()方法,获取向文件写入数据的输出流。方法需要2个参数:第一个参数是文件名;第二个参数是写入模式,主要有2种(MODE_PRIVATEMODE_APPEND)。
MODE_PRIVATE:是默认的写入模式,该模式下,如果存在同名的时候,会将写入的内容完全覆盖源文件的内容。
MODE_APPEND:如果已存在该文件,就往该文件追加写入数据;如果文件不存在,则新建该文件。

    private void save(String content) {
        FileOutputStream out = null;
        BufferedWriter writer = null;
        try {
            out = openFileOutput("data", Context.MODE_PRIVATE);
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.write(content);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(null != writer) {
                    writer.close();
                }
                if(null != out) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

从文件中读取数据

Context类中提供openFileInput()方法,获取从文件读取数据的输入流。

    private String load() {
        FileInputStream in = null;
        BufferedReader reader = null;
        StringBuilder sbd = new StringBuilder();
        try {
            in = openFileInput("data");
            reader = new BufferedReader(new InputStreamReader(in));
            String line = "";
            while (null != (line = reader.readLine())) {
                sbd.append(line);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(null != reader) {
                    reader.close();
                }
                if(null != in) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return sbd.toString();
    }

- 阅读剩余部分 -

在上篇文章中对四大组件之Activity进行进行总结,这篇文章是对另一个四大组件Broadcast 广播进行总结。

广播类型

广播可以分为两种类型:

  • Normal broadcasts 标准广播:完全异步的,广播发出后,所有的广播接收器几乎都会在同一时刻收到这条广播消息。它的特点是,广播效率高,但是无法被截断。
    NormalBroadcasts.jpg
  • Ordered broadcasts 有序广播:同步执行的,广播发出后,同一时刻只能有一个广播接收器能够收到这条广播消息,只有当该广播接收器的逻辑处理完成后,广播才会继续传递给下一个广播接收器。优先级越高的广播接收器会优先接收到广播消息,另外广播接收器还可以对广播进行截断,这样后面的广播接收器就无法收到该广播消息。
    OrderBroadcasts.jpg

- 阅读剩余部分 -

Fragment碎片是可嵌入到活动中的UI片段,能让程序更加合理利用大屏幕。

Fragment基本用法

在一个活动中添加2个Fragment,这两个Fragment平方活动空间。
1、创建左侧的Fragment布局left_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Button"/>
</LinearLayout>

创建右侧的Fragment布局right_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#00ff00"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="20sp"
        android:text="This is right fragment."/>
</LinearLayout>

2、创建Fragment类
左侧Fragment类LeftFragment:

public class LeftFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.left_fragment, container, false);
        return view;
    }
}

右侧Fragment类RightFragment:

public class RightFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.right_fragment, container, false);
        return view;
    }
}

3、在活动布局中添加Fragment

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/left_fragment"
        android:name="com.jyoryo.app.android.study.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>

    <fragment
        android:id="@+id/right_fragment"
        android:name="com.jyoryo.app.android.study.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
</LinearLayout>

- 阅读剩余部分 -

通过Android Studio可视化编辑器可以通过拖拽方式完成界面布局,但这种方式生成的界面通常不具有很好的屏幕适配性,而且编写较为复杂界面时,通过可视化编辑器实现效果也不好。建议新手还是通过xml方式编写界面。通过Android Studio强大的智能提示,使用xml方式编写界面也是很方便的。

Android常用控件

TextView

TextView应该是最常用的一个控件,用来显示一段文本内容。简单的TextView用法如下:

    <TextView
        android:id="@+id/text_view_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="This is a TextView1!" />

    <TextView
        android:id="@+id/text_view_2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="This is a TextView2!" />

    <TextView
        android:id="@+id/text_view_3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="24sp"
        android:textColor="#00ff00"
        android:text="This is a TextView3!" />
  • android:id:给控件定义一个唯一标识符;
  • android:layout_widthandroid:layout_height:定义控件宽度和高度,可选择有wrap_contentmatch_parent,和fill_parent,一般用wrap_contentmatch_parent
  • android:text:定义控件显示的文本内容;
  • android:gravity:指定控件文字的对齐方式,可选择有topbottomleftrightcentercenter_horizontalcenter_vertical等,可以用管道符"|"来同时指定多个值;
  • android:textSize:定义文字的大小;
  • android:textColor:定义文字颜色。

- 阅读剩余部分 -

Android四大组件:Activity(活动)Service(服务)Broadcast Receiver(广播接收器)Content Provider(内容提供器)

  • Activity(活动)
    Activity活动是所有Android应用的界面显示,凡是应用中能看到的内容,都是放在活动中。
  • Service(服务)
    Service服务无法看到,但是它会一直在后台默默地运行。
  • Broadcast Receiver(广播接收器)
    Broadcast Receiver广播接收器运行应用接收来自各处的广播消息,比如:电话、短信等。我们应用也可以自己定义广播消息对外广播发送。
  • Content Provider(内容提供器)
    Content Provider内容提供器为应用之间共享数据提供了可能。比如访问系统电话簿中的联系人,就需要通过内容提供器来实现。

本文是对Activity(活动)的总结。

活动的创建

  1. 创建活动Activity的类。例如:

    public class MainActivity extends AppCompatActivity {
       private static final String TAG = "MainActivity";
       
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
       }
    }
  2. 创建活动的布局(视图)

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="match_parent">
    
       <TextView
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:text="This is a activity!" />
    </LinearLayout>
  3. AndroidManifest.xml中进行注册。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.jyoryo.app.android.study">
    
       <application
           android:allowBackup="true"
           android:icon="@mipmap/ic_launcher"
           android:label="@string/app_name"
           android:roundIcon="@mipmap/ic_launcher_round"
           android:supportsRtl="true"
           android:theme="@style/AppTheme">
           <activity android:name=".MainActivity">
               <intent-filter>
                   <action android:name="android.intent.action.MAIN" />
                   <category android:name="android.intent.category.LAUNCHER" />
               </intent-filter>
           </activity>
       </application>
    
    </manifest>

其中标签activity ,就是申明注册Activity。android:name,用来指定具体注册的活动。比如上面实例中.MainActivity,标识活动的类名com.jyoryo.app.android.study.MainActivity。由于标签manifest中已经通过package申明了包名,因此注册活动时,包名这一部分就可以省略了。

- 阅读剩余部分 -

支持伪静态

为了使typecho支持伪静态,即访问文章时浏览器路径不显示index.php,我们可以在nginx配置文件中按照下面设置方法进行设置:

location / {
    index index.html index.php;
    if (-f $request_filename/index.html) {
        rewrite (.*) $1/index.html break;
    }
    if (-f $request_filename/index.php) {
        rewrite (.*) $1/index.php;
    }
    if (!-f $request_filename) {
        rewrite (.*) /index.php;
    }
}

支持SSL

前提先准备好自己ssl证书。(可以通过Let's Encrypt申请免费的证书)。
nginx配置文件配置

server {
    listen 80;
    server_name www.domain.com;
    ## 301 跳转到https
    return 301 https://$host$request_uri;
}

server {
    server_name www.domain.com;
    root /var/www/project;
    index index.php index.html index.htm;
    
    location ~* \.(gif|jpg|png|jpeg|ico)$ {
        expires max;
    }

    ## 支持伪静态
    location / {
    index index.html index.php;
    if (-f $request_filename/index.html) {
        rewrite (.*) $1/index.html break;
    }
    if (-f $request_filename/index.php) {
        rewrite (.*) $1/index.php;
    }
    if (!-f $request_filename) {
        rewrite (.*) /index.php;
    }
    }

    location ~ .*\.php(\/.*)*$ {
    # fastcgi_index   index.php;
    include snippets/fastcgi-php.conf;
        fastcgi_pass 127.0.0.1:9000;
    # fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        # include fastcgi_params;
    }

    listen 443 ssl;
    ssl_certificate cert/domain.com.cert;
    ssl_certificate_key cert/domain.com.key;
    include cert/options-ssl-nginx.conf;
    ssl_dhparam cert/ssl-dhparams.pem;
}

上面的options-ssl-nginx.conf内容为:

ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1440m;
ssl_session_tickets off;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;

ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS";

通过Android Studio创建的Android项目,都有固定的目录结构。

一、项目根目录结构及说明

如下图所示:
AndroidProjectStructure.jpg

  1. .gradle.idea
    这两个目录下都是Android Studio自动生成的一些文件,不用关心,当然也不要去手动编辑。
  2. app
    项目代码、资源等内容几乎都是在这个目录下,我们的开发也基本都是在这个目录下进行。我们会在本会的第二部分还会对该目录下的内容单独说明。
  3. gradle
    这个目录下包含了gradle wrapper的配置文件,使用gradle wrapper的方式不需要提前将gradle下载好,而是自动根据本地缓存情况来确定是否需要联网下载gradle。Android Studio默认没有启用gradle wrapper的方式,如果需要打开,可以点击Android Studio导航栏→File→Settings→Build,Execution,Deployment→Gradle,进行配置修改。
  4. .gitignore
    这个文件是用来指定目录或文件排除在git版本控制之外的。
  5. build.gradle
    项目全局的gradle的构建脚本,通常该文件的内容不需要修改。
  6. gradle.properties
    项目全局的gradle配置文件,在这里配置的属性将会影响到项目中所有gradle编译脚本。
  7. gradlewgradlew.bat
    这两个文件是用在命令行界面中执行gradle命令的。(gradlew:是LinuxMac系统中使用;gradlew.bat:是Windows系统中使用的。)
  8. local.properties
    用于指定本机中的Android SDK路径,通过内容都是自动生成的,我们不需要修改。除非本机中的Android SDK位置发生了变化,可以在这个文件中的路径改为新的位置即可。
  9. settings.gradle
    这个文件指定项目中所有引入的模块。默认新创建的项目只有一个模块,一次该文件仅引入了app这个模块。通常情况下模块引入都是自动完成的,不需要我们手动去修改这个文件。

二、项目模块目录结构及说明

如下图示:
AndroidModuleStructure.jpg

  1. build
    这个目录主要包含了一些在编译时自动生成的文件,一般我们不要关心。
  2. libs
    如果项目中需要使用第三方jar包,就需要将这些jar包都放在这个目录。放在这个目录下的jar包都会被自动添加到构建路径里。
  3. src/androidTest
    用来编写AndroidTest测试用例的,可以对项目进行一些自动化测试。
  4. src/main/java
    这个目录是放置我们所有Java代码的地方。
  5. src/main/res
    这个目录下的内容比较多。项目中用到的所有图片、布局、字符串等资源都放在这个目录下。这个目录下还有很多子目录,图片放在drawable目录下,布局放在layout目录下,字符串放在values目录下。
  6. src/main/AndroidManifest.xml
    整个Android项目的配置文件,程序中定义的所有四大组件都需要在这个文件里注册,另外还可以在这个文件中给应用添加权限申明。
  7. src/test
    该目录用来编写Unit Test测试用例的。
  8. gitignore
    这个文件用于将app模块内指定的目录或文件排除在版本控制之外,作用域上层目录的.gitignore类似。
  9. app.iml
    IDE自动生成的文件。
  10. build.gradle
    是app模块的gradle构建脚本,这个文件会指定很多项目构建相关的配置。
  11. proguard-rules.pro
    这个文件用于指定项目代码的混淆规则。如果不希望代码被别人破解,通常会将代码进行混淆。