Android上传手机图片到服务器
1、整体流程2、页面布局3、选择图片流程实现演示结果完整代码 4、路径转换路径转换Utils工具类权限申请完整代码 5、创建文件6、服务器端7、传输8、演示9、完整代码目录结构AndroidManifest.xml布局文件activity_main.xml传输文件工具类HttpUtil路径转换工具类UtilsMainActivity类
1、整体流程
通过安卓app选取本地图片然后上传到服务器的整体流程步骤如下:
2、页面布局
样式
布局代码
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" > <ImageView android:layout_width="200dp" android:layout_height="200dp" android:background="#555555" android:id="@+id/iv_image"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/xz" android:text="选择图片"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/sc" android:text="上传图片"/> </LinearLayout>
id:iv_image用于呈现选择的图片
id:xz用于选择图片的按钮
id:sc用于上传的按钮
3、选择图片
流程
流程:点击“选择图片”在本机选取图片然后呈现到ImageView中(这个操作过程是不需要申请任何权限的)
实现
(1)获取“选择图片”按钮,并设置监听事件。
xz = (Button) findViewById(R.id.xz);//选择照片按钮xz.setOnClickListener(this);//设置监听
(2)获取ImageView,便于之后呈现图片
iv_image = (ImageView) findViewById(R.id.iv_image);//展示图片按钮
(3)点击“选择图片”后操作,选取图片
点击事件
@Override public void onClick(View view) { switch (view.getId()) { case R.id.xz: xzImage();//选择图片 break; } }
xzImage()函数进行图片选择
private void xzImage() { Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent,CHOOSE_PHOTO); // 打开本地存储 //CHOOSE_PHOTO:全局常量,标识}
CHOOSE_PHOTO:是一个全局常量,用于标识这是选择图片的这个操作,便于在回调函数中使用。
public static final int CHOOSE_PHOTO = 1;
(4)重写选择图片后的回调函数
当在手机上选择完图片后会回调onActivityResult函数,将一下“选择的信息”返回供用户操作。
requestCode:标识码
data:选择的图片的信息
data.getData()可以获取到图片的路径,但是是虚拟路径不是真实的存储路径,虚拟路径在ImageView组件中可以使用,但是如果通过虚拟路径进行创建文件(new File(“路径”))是不可能的。
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); //requestCode:标识码 //data:选择的图片的信息 switch (requestCode) { case CHOOSE_PHOTO: //显示图片 iv_image.setImageURI(data.getData());//放在ImageView中呈现 break; default: break; } }
以上操作全部是无需权限申请的
演示结果
完整代码
上述操作的完整代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button xz; private ImageView iv_image; public static final int CHOOSE_PHOTO = 1;//标识选择图片 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //按钮 xz = (Button) findViewById(R.id.xz);//选择照片按钮 //图片 iv_image = (ImageView) findViewById(R.id.iv_image);//展示图片 //设置点击事件监听 xz.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.xz: xzImage(); break; } } private void xzImage() { Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent,CHOOSE_PHOTO); // 打开本地存储 } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case CHOOSE_PHOTO://判断是不是选择照片后的操作 //显示图片 iv_image.setImageURI(data.getData()); break; default: break; } }}
4、路径转换
由于data.getData()获取到的是图片的虚拟路径,所以我们需要对路径进行路径转换,从虚拟路径到真实路径的转换。(通过Utils.getRealPath(this, data)方法进行转换)
Utils.getRealPath(this, data)方法是封装的工具类
路径转换
Utils工具类
public class Utils { public static String getRealPath(Context context,Intent data){ // 判断手机系统版本号 if (Build.VERSION.SDK_INT >= 19) { // 4.4及以上系统使用这个方法处理图片 return handleImageOnKitKat(context,data); } else { // 4.4以下系统使用这个方法处理图片 return handleImageBeforeKitKat(context,data); } } @TargetApi(19) private static String handleImageOnKitKat(Context context,Intent data) { String imagePath = null; Uri uri = data.getData(); if (DocumentsContract.isDocumentUri(context, uri)) { // 如果是document类型的Uri,则通过document id处理 String docId = DocumentsContract.getDocumentId(uri); if("com.android.providers.media.documents".equals(uri.getAuthority())) { String id = docId.split(":")[1]; // 解析出数字格式的id String selection = MediaStore.Images.Media._ID + "=" + id; imagePath = getImagePath(context,MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public downloads"), Long.valueOf(docId)); imagePath = getImagePath(context,contentUri, null); } } else if ("content".equalsIgnoreCase(uri.getScheme())) { // 如果是content类型的Uri,则使用普通方式处理 imagePath = getImagePath(context,uri, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) { // 如果是file类型的Uri,直接获取图片路径即可 imagePath = uri.getPath(); } //displayImage(imagePath); // 根据图片路径显示图片 return imagePath; } private static String handleImageBeforeKitKat(Context context,Intent data) { Uri uri = data.getData(); String imagePath = getImagePath(context,uri, null); return imagePath; } @SuppressLint("Range") private static String getImagePath(Context context,Uri uri, String selection) { String path = null; // 通过Uri和selection来获取真实的图片路径 Cursor cursor = context.getContentResolver().query(uri, null, selection, null, null); if (cursor != null) { if (cursor.moveToFirst()) { path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); } return path; }}
权限申请
由于在工具类中进行路径转换时候需要用到存储权限,所以我们在之前代码的基础上加上动态权限申请
注意:是在点击“选择图片”后进行权限申请,申请完后再去选择图片
顺序是这样子的:点击“选择图片按钮” == > 权限申请 ==》选择图片·
(1)在Manifest.xml中加入权限声明
<!--写--><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><!--读--><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
(2)在onclick处加上权限检测、申请步骤
@Override public void onClick(View view) { switch (view.getId()) { case R.id.xz: if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_PERMISSION); } else { xzImage(); } break; } }
Manifest.permission.WRITE_EXTERNAL_STORAGE既包括读权限又包括写权限,所以在Manifest.xml中才声明了两个
STORAGE_PERMISSION:是一个全局常量,用于标识申请的是什么权限,方便在权限的回调函数中使用。
public static final int STORAGE_PERMISSION = 1;
(3)添加权限的回调函数
//选择权限后的回调函数 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case STORAGE_PERMISSION: //检查是否有读取存储卡的权限,如果有则选择图片,如果没有则提示 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { xzImage(); } else { Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show(); } break; default: } }
STORAGE_PERMISSION:标识申请的是存储权限,是全局常量。
(4)测试
我们在onActivityResult中输出虚拟路径和真实路径看一下(如果不做上述的权限申请,则在调用Utils.getRealPath(this, data)方法进行路径转换时会报错)
//选择图片后的回调函数 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case CHOOSE_PHOTO: //显示图片 iv_image.setImageURI(data.getData()); System.out.println("图片在手机上的虚拟路径为:"+data.getData()); String realPath = Utils.getRealPath(this, data); System.out.println("图片在手机上的真实路径为:"+realPath); break; default: break; } }
申请权限
输出
完整代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button xz; private ImageView iv_image; public static final int CHOOSE_PHOTO = 1;//标识选择图片 public static final int STORAGE_PERMISSION = 1;//标识权限申请 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //按钮 xz = (Button) findViewById(R.id.xz);//选择照片按钮 //图片 iv_image = (ImageView) findViewById(R.id.iv_image);//展示图片 //设置点击事件监听 xz.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.xz: if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_PERMISSION); } else { xzImage(); } break; } } private void xzImage() { Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent,CHOOSE_PHOTO); // 打开本地存储 } //选择图片后的回调函数 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case CHOOSE_PHOTO: //显示图片 iv_image.setImageURI(data.getData()); System.out.println("图片在手机上的虚拟路径为:"+data.getData()); String realPath = Utils.getRealPath(this, data); System.out.println("图片在手机上的真实路径为:"+realPath); break; default: break; } } //选择权限后的回调函数 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case STORAGE_PERMISSION: //检查是否有读取存储卡的权限,如果有则选择图片,如果没有则提示 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { xzImage(); } else { Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show(); } break; default: } }}
5、创建文件
当我们通过Utils.getRealPath(this, data)方法获取到真实的图片路径后就可以通过new File(路径的方式将图片封装成File对象了)
String realPath = Utils.getRealPath(this, data);file = new File(realPath);
这里的file是全局变量,便于后续上传的时候使用
private File file=null;
所以此时选择图片后的回调函数变为如下:
//选择图片后的回调函数 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case CHOOSE_PHOTO: //显示图片 iv_image.setImageURI(data.getData()); //System.out.println("图片在手机上的虚拟路径为:"+data.getData()); String realPath = Utils.getRealPath(this, data); file = new File(realPath); //System.out.println("图片在手机上的真实路径为:"+realPath); break; default: break; } }
至此从相册选择图片到将图片创建成file对象已经全部完成。
这半部分全部完成
6、服务器端
为便于测试,该项目的服务器代码比较简单,通过一个接口接收文件(uploadfile),以及传过来的其余参数(name)。输出其余参数,并将图片存储到项目的当前目录下命名为a.jpg
@RestController@RequestMapping("/test")@CrossOriginpublic class TestController { /** * * @param uploadfile 接收文件 * @param name 接收其余参数 * @return * @throws IOException */ @PostMapping("/upload") public String upload(MultipartFile uploadfile,String name) throws IOException { //1、输出测试 System.out.println("=============="); System.out.println(uploadfile); System.out.println(name); System.out.println(uploadfile.getName()); System.out.println(uploadfile.getSize()); //2、将上传的图片存储到硬盘 InputStream inputStream = uploadfile.getInputStream(); FileChannel inChannel = (FileChannel) Channels.newChannel(inputStream); FileChannel outChannel = new FileOutputStream("./a.jpg").getChannel();//当前目录下,命名为a.jpg inChannel.transferTo(0,inChannel.size(),outChannel); //3、关闭流 inChannel.close(); outChannel.close(); inputStream.close(); //4、返回成功信息 return "success"; }}
(springboot项目)
目前为止服务器端的代码也以及全部搞定
7、传输
Android端的东西全部搞定,服务器端的东西也全部搞定,接下来就是通信了,也就是将文件从Android端传输到服务器端。
传输文件采用okhttp进行数据的传输
(1)权限申请
由于传输的时候使用到网络,所以在Manifest.xml中声明网络权限
<!-- 网络--> <uses-permission android:name="android.permission.INTERNET" />
并在Manifest.xml中加入这个,我们不采用https进行传输
(2)引入okhttp的依赖
//okhttp implementation 'com.squareup.okhttp3:okhttp:3.14.9'
刷新后便可以将okhttp的依赖加载到本地
(3)传输文件的工具类
通过okhttp封装成上传文件的工具类
public class HttpUtil { /** * * @param address 服务器地址 * @param requestBody 请求体数据 * @param callback 回调接口 */ public static void uploadFile(String address,RequestBody requestBody ,okhttp3.Callback callback){ //发送请求 OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(60, TimeUnit.SECONDS)//设置连接超时时间 .readTimeout(60, TimeUnit.SECONDS)//设置读取超时时间 .build(); Request request = new Request.Builder() .url(address) .post(requestBody) .build(); client.newCall(request).enqueue(callback); }}
(4)上传函数
获取“上传按钮”并设置监听事件
sc = (Button) findViewById(R.id.sc);//上传按钮sc.setOnClickListener(this);//上传
在点击事件中添加scImage()函数,在点击"上传图片"按钮后触发
onclick中
@Override public void onClick(View view) { switch (view.getId()) { case R.id.xz: if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_PERMISSION); } else { xzImage(); } break; case R.id.sc: scImage(); break; } }
scImage()函数
private void scImage() { //1、创建请求体 RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM)//请求类型 .addFormDataPart("name", "lisi")//参数1 .addFormDataPart("uploadfile", "uploadfile", RequestBody.create(MediaType.parse("*/*"), file)) // 第一个参数传到服务器的字段名,第二个你自己的文件名,第三个MediaType.parse("*/*")数据类型,这个是所有类型的意思,file就是我们之前创建的全局file,里面是创建的图片 .build(); //2、调用工具类上传图片以及参数 HttpUtil.uploadFile("http://你的服务器IP:8080/test/upload", requestBody, new Callback() { //请求失败回调函数 @Override public void onFailure(Call call, IOException e) { System.out.println("============="); System.out.println("异常::"); e.printStackTrace(); } //请求成功响应函数 @Override public void onResponse(Call call, Response response) throws IOException { showResponse(response.body().string());//在主线程中显示提示框 } }); }
注意:安卓端的字段名要与服务器端接收的字段名字一样
由于在子线程不能操作ui,所以这里调用showResponse在主线程中提示,响应结果。
showResponse()
//ui操作,提示框 private void showResponse(final String response) { runOnUiThread(new Runnable() { @Override public void run() { // 在这里进行UI操作,将结果显示到界面上 Toast.makeText(MainActivity.this, response, Toast.LENGTH_SHORT).show(); } }); }
8、演示
至此已经完成了所有步骤“Android端”、“服务器端”、“传输数据”的代码编写。
我们将服务器端代码在服务器部署好,然后在Android端输入对应的ip,进行测试。
服务的“当前文件夹目录”
传输
传输完后
可以拉到本地打开看一下
9、完整代码
目录结构
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 网络--> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_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/Theme.MyApplication" android:usesCleartextTraffic="true" > <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <meta-data android:name="android.app.lib_name" android:value="" /> </activity> </application></manifest>
布局文件activity_main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" > <ImageView android:layout_width="200dp" android:layout_height="200dp" android:background="#555555" android:id="@+id/iv_image"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/xz" android:text="选择图片"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/sc" android:text="上传图片"/></LinearLayout>
传输文件工具类HttpUtil
public class HttpUtil { /** * * @param address 服务器地址 * @param requestBody 请求体数据 * @param callback 回调接口 */ public static void uploadFile(String address,RequestBody requestBody ,okhttp3.Callback callback){ //发送请求 OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(60, TimeUnit.SECONDS)//设置连接超时时间 .readTimeout(60, TimeUnit.SECONDS)//设置读取超时时间 .build(); Request request = new Request.Builder() .url(address) .post(requestBody) .build(); client.newCall(request).enqueue(callback); }}
路径转换工具类Utils
public class Utils { public static String getRealPath(Context context,Intent data){ // 判断手机系统版本号 if (Build.VERSION.SDK_INT >= 19) { // 4.4及以上系统使用这个方法处理图片 return handleImageOnKitKat(context,data); } else { // 4.4以下系统使用这个方法处理图片 return handleImageBeforeKitKat(context,data); } } @TargetApi(19) private static String handleImageOnKitKat(Context context,Intent data) { String imagePath = null; Uri uri = data.getData(); if (DocumentsContract.isDocumentUri(context, uri)) { // 如果是document类型的Uri,则通过document id处理 String docId = DocumentsContract.getDocumentId(uri); if("com.android.providers.media.documents".equals(uri.getAuthority())) { String id = docId.split(":")[1]; // 解析出数字格式的id String selection = MediaStore.Images.Media._ID + "=" + id; imagePath = getImagePath(context,MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public downloads"), Long.valueOf(docId)); imagePath = getImagePath(context,contentUri, null); } } else if ("content".equalsIgnoreCase(uri.getScheme())) { // 如果是content类型的Uri,则使用普通方式处理 imagePath = getImagePath(context,uri, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) { // 如果是file类型的Uri,直接获取图片路径即可 imagePath = uri.getPath(); } //displayImage(imagePath); // 根据图片路径显示图片 return imagePath; } private static String handleImageBeforeKitKat(Context context,Intent data) { Uri uri = data.getData(); String imagePath = getImagePath(context,uri, null); return imagePath; } @SuppressLint("Range") private static String getImagePath(Context context,Uri uri, String selection) { String path = null; // 通过Uri和selection来获取真实的图片路径 Cursor cursor = context.getContentResolver().query(uri, null, selection, null, null); if (cursor != null) { if (cursor.moveToFirst()) { path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); } return path; }}
MainActivity类
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button xz; private Button sc; private ImageView iv_image; public static final int CHOOSE_PHOTO = 1; public static final int STORAGE_PERMISSION = 1; private File file=null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //按钮 xz = (Button) findViewById(R.id.xz);//选择照片按钮 sc = (Button) findViewById(R.id.sc);//上传按钮 //图片 iv_image = (ImageView) findViewById(R.id.iv_image);//展示图片 //设置点击事件监听 xz.setOnClickListener(this);//选择 sc.setOnClickListener(this);//上传 } @Override public void onClick(View view) { switch (view.getId()) { case R.id.xz: if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_PERMISSION); } else { xzImage(); } break; case R.id.sc: scImage(); break; } } private void xzImage() { Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent,CHOOSE_PHOTO); // 打开本地存储 } private void scImage() { RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("name", "lisi") .addFormDataPart("uploadfile", "uploadfile", RequestBody.create(MediaType.parse("*/*"), file)) // 第一个参数传到服务器的字段名,第二个你自己的文件名,第三个MediaType.parse("*/*")数据类型,这个是所有类型的意思,file就是我们之前创建的全局file,里面是创建的图片 .build(); HttpUtil.uploadFile("http://你自己的ip:8080/test/upload", requestBody, new Callback() { @Override public void onFailure(Call call, IOException e) { System.out.println("============="); System.out.println("异常::"); e.printStackTrace(); //Toast.makeText(MainActivity.this, "上传异常", Toast.LENGTH_SHORT).show(); } @Override public void onResponse(Call call, Response response) throws IOException { showResponse(response.body().string()); //Toast.makeText(MainActivity.this, "上传成功", Toast.LENGTH_SHORT).show(); } }); } //选择图片后的回调函数 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case CHOOSE_PHOTO: //显示图片 iv_image.setImageURI(data.getData()); //System.out.println("图片在手机上的虚拟路径为:"+data.getData()); String realPath = Utils.getRealPath(this, data); file = new File(realPath); //System.out.println("图片在手机上的真实路径为:"+realPath); break; default: break; } } //选择权限后的回调函数 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case STORAGE_PERMISSION: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { xzImage(); } else { Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show(); } break; default: } } //ui操作,提示框 private void showResponse(final String response) { runOnUiThread(new Runnable() { @Override public void run() { // 在这里进行UI操作,将结果显示到界面上 Toast.makeText(MainActivity.this, response, Toast.LENGTH_SHORT).show(); } }); }}