Android应用开发中,常使用Environment类去获取外部存储目录,在访问外部存储之前一定要先判断外部存储是否已经是可使用(已挂载&可使用)状态,并且需要在AndroidManifest.xml文件中添加外部存储读和写的权限
Environment
类中提供了几个静态常量用于标识外部存储的状态,这些状态都是String类型
MEDIA_BAD_REMOVAL 在没有挂载前存储媒体已经被移除。 MEDIA_CHECKING 正在检查存储媒体。
MEDIA_MOUNTED 存储媒体已经挂载,并且挂载点可读/写。 MEDIA_MOUNTED_READ_ONLY
存储媒体已经挂载,挂载点只读。 MEDIA_NOFS 存储媒体是空白或是不支持的文件系统。 MEDIA_REMOVED 存储媒体被移除。
MEDIA_SHARED 存储媒体正在通过USB共享。 MEDIA_UNMOUNTABLE 存储媒体无法挂载。
MEDIA_UNMOUNTED 存储媒体没有挂载。
可以通过静态方法getExternalStorageState()
来获取外部存储的状态,如果程序需要在外部存储里面读写数据,
必须要先判断:
if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable());
下面这篇文章写得外部存储的注意很全面,有很大的参考价值
https://blog.csdn.net/FDGFGFDGFD/article/details/80434520
如果在Android6.0(API:23)
以下的版本中申请权限只需要AndroidMainfest
文件中中申请权限就行,只需要这样写就行了
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication">
<!-- 声明权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE "/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<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=".ActivityExample">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="cn.itcast.start_activity" />
<!-- 当设置为LAUNCHER的时候,应用默认打开的界面-->
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 配置Activity-->
<!-- ?-->
<activity android:name=".SecondActivity"></activity>
</application>
</manifest>
上面不难看出,在manifest>
中直接添加的读取和写入的权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE "/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
安卓6.0以上的版本怎么办,就需要动态申请权限了
下面添加了一个显示跳转与隐式跳转的两种方式,是activity组件的知识,不必在意在此处
package com.example.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
public class ActivityExample extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_example);
findViewById(R.id.start).setOnClickListener(this);
findViewById(R.id.start).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start: //显示意图
Intent intent = new Intent(ActivityExample.this,MainActivity.class);
startActivity(intent);
break;
case R.id.start2: //隐示意图
Intent intent1 = new Intent();
intent1.setAction("cn.itcast.start_activity");
startActivity(intent1);
break;
}
}
}
下面是自己画的界面,额,有点丑
实现代码:(采用的ConstraintLayout线性布局)
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity"
android:gravity="center">
<CheckBox
android:id="@+id/isAutoLogin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="取消用户名"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.948"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.553" />
<CheckBox
android:id="@+id/isRememberUsername"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="记住用户名"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.572"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.553" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码:"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.186"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.449" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="用户名:"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.185"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.352" />
<EditText
android:id="@+id/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPassword"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.317"
app:layout_constraintStart_toEndOf="@+id/textView2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.457" />
<EditText
android:id="@+id/usename"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="textPersonName"
android:text="Name"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.022"
app:layout_constraintStart_toEndOf="@+id/textView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.358" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="注册"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.746"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.736" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="登录"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.343"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.736" />
</androidx.constraintlayout.widget.ConstraintLayout>
下面就是实现动态实现申请权限的方法:(单独一个文件)
package com.example.myapplication;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class MainActivity extends AppCompatActivity {
//自定义文本、按钮、多选框的变量
private EditText usename;
private EditText password;
private Button button;
private Button button2;
private CheckBox isRememberUsername;
private CheckBox isAutoLogin;
@Override
public void onCreate(Bundle savedInstanceState) {
//下面的是新建程序自带的,实现父类中的onCreate方法
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取R文件的各种控件赋值给上面的控件
//因为上面定义变量为成员变量,所以此处不必写类名
//定义成员变量的目的是因为在在此文件下都可以私用
usename = findViewById(R.id.usename);
password = findViewById(R.id.password);
button = findViewById(R.id.button2);
button2 = findViewById(R.id.button);
isRememberUsername = findViewById(R.id.isRememberUsername);
isAutoLogin = findViewById(R.id.isAutoLogin);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 这里是内部存储,不需要申请权限
这里是开启Activity跳转的写法
// 采用的intent类进行开启的
// 参数一:Activity未跳转的界面
// 参数二:Activity将要跳转的界面
// Intent intent = new Intent(MainActivity.this,SecondActivity.class);
// startActivity(intent);
文件读取,采用File流与getFilesDir()方法打开将要存储的文件
// File fileDir = getFilesDir();
//给文件起个名字login.data
// File destFile = new File(fileDir,"login.data");
// try {
// BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(destFile)));
//这里需要用包装类的valueOf()方法来进行转化,因为有的输入账号和密码是数字,需要转换为字符串
// writer.write(String.valueOf(usename));
//这里需要不断地刷新换行,一行一行的读取
// writer.newLine();
//
// writer.write(String.valueOf(password));
// writer.newLine();
//
// writer.write(String.valueOf(isRememberUsername));
// writer.newLine();
//
// writer.write(String.valueOf(isAutoLogin));
//
// writer.newLine();
//这里需要刷新一下保证全部读取成功
// writer.flush();
//严格的编码需要关闭流
//
// Log.d("TEST","操作成功");
//
// } catch (Exception e) {
// Log.d("TEST",e.getMessage());
// e.printStackTrace();
// }
//检查是否有写入存储卡的权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//全部所需要的权限的数组,这里只添加了一个写入权限
String[] permissions = {
Manifest.permission.WRITE_EXTERNAL_STORAGE
};
//用来表示,是否用有全部的权限,是为true,不是为false
boolean granted = true;
//对一个权限进行验证,for-Each循环
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(view.getContext(), permission) != PackageManager.PERMISSION_DENIED) {
granted = false;
break;
}
}
//编写两个方法,在下面,有权限就是直接写入,没有权限再
//验证是否拥有权限
if (granted) {
//存储用户登录信息
saveUserLogin();
} else {
//申请权限
requestPermissions(permissions, 0xA1);
}
} else {
//存储用户登录信息
saveUserLogin();
}
}
});
//为登录设置一事件监听的提示
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
isRememberUsername.setChecked(false);
isAutoLogin.setChecked(false);
usename.setText("");
password.setText("");
Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
}
});
// 加载上次保存的信息
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(openFileInput("login.data")));
String un = reader.readLine();
String pw = reader.readLine();
} catch (Exception e) {
Log.d("TEST", e.getMessage());
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
//对应上面申请外部存储器的权限
if (requestCode == 0xA1) {
boolean granted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_DENIED) {
granted = false;
break;
}
}
if (granted) {
//确定获取了权限则进行写入
saveUserLogin();
} else {
Toast.makeText(MainActivity.this, "未能能获取权限,不能进行此操作", Toast.LENGTH_SHORT).show();
}
}
}
private void saveUserLogin() {
//登录信息存储在外部存储器中
if (Environment.getExternalStorageState().equalsIgnoreCase(Environment.MEDIA_BAD_REMOVAL)) {
File root = Environment.getExternalStorageDirectory();
Log.d("TEST", root.getAbsolutePath());
Log.d("TEST", "外部存储可用");
} else {
Log.d("TEST", "外部存储不可用");
}
}
}